systemd unit stops when required unit is stopped, but then does not start again when required unit starts
I've got a unit that I'll just call eth-service
that requires another unit that I'll call eth-server
. In the unit file of eth-service
I have
Requires=eth-server.service
After=eth-server.service
Now, when I run systemctl stop eth-server
, then eth-service
stops as well, as it should. However, later when I run systemctl start eth-server
, unfortunately eth-service
remains inactive, so I have to remember to re-start it.
I'm trying to make my unit file setup as idiot-proof as possible (especially against myself) - is it possible to set up my unit files so that when the server starts up, the service does too? If I were to put into my eth-server
unit file:
Requires=eth-service.service
Before=eth-service.service
so that they mutually require each other, that would cause the service to start up when the server does, but it also means that when I run systemctl stop eth-service
then eth-server
stops too, since it now requires the service, which is not desirable.
Let me see if I understood the use-case:
- Starting
eth-service
should start botheth-server
andeth-service
. - Starting
eth-server
should start botheth-server
andeth-service
. - Stopping
eth-server
should stop botheth-server
andeth-service
. - Stopping
eth-service
should ONLY stopeth-service
and leaveeth-server
running.
You can achieve it by adding WantedBy=eth-server.service
to the [Install] section of the eth-service.service
file, or Wants=eth-service.service
to the [Unit] section of the eth-server.service
.
According to the systemd documentation Wants
is a weaker version of the Requires
, and it seems like if Wanted
unit is stopped, the unit that requested it is not affected. But there might be an undesirable side-effect in this setup where eth-server
will start no matter if eth-service
fails to start or not.
Unit file examples
Following sample unit files can be saved in the home directory (~/.config/systemd/user
) and used with systemctl --user
parameter.
Services described by those unit files don't do anything, but the RemainAfterExit=yes
forces systemd to assume that the service is still running, even though the /bin/true
command does not hang around after it is executed.
etc-service.service
[Unit]
Description=eth Service
Requires=eth-server.service
[Service]
Type=oneshot
ExecStart=/bin/true
RemainAfterExit=yes
[Install]
WantedBy=eth-server.service
eth-server.service
[Unit]
Description=eth Server
[Service]
Type=oneshot
ExecStart=/bin/true
RemainAfterExit=yes
Enable the eth-service.service
module with systemctl --user enable eth-service.service
.
Following is the output of tests with starting/stopping services:
Initial State
$> systemctl --user status eth-service
● eth-service.service - eth Service
Loaded: loaded (/home/ektich/.config/systemd/user/eth-service.service; enabled; vendor preset: enabled)
Active: inactive (dead)
$> systemctl --user status eth-server
● eth-server.service - eth Server
Loaded: loaded (/home/ektich/.config/systemd/user/eth-server.service; static; vendor preset: enabled)
Active: inactive (dead)
System is returned into initial state before commands in following two sections are executed.
systemctl --user start eth-service.service
Both server
and service
are started (ignore the exited
lines: both server and service are executions of /bin/true, which exits once it is finished, but the unit files are written so that systemd will think services are running until they are stopped)
$> systemctl --user start eth-service.service
$> systemctl --user status eth-service
● eth-service.service - eth Service
Loaded: loaded (/home/ektich/.config/systemd/user/eth-service.service; enabled; vendor preset: enabled)
Active: active (exited) since Tue 2018-05-29 20:50:01 IST; 28s ago
Process: 1578 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 1578 (code=exited, status=0/SUCCESS)
$> systemctl --user status eth-server
● eth-server.service - eth Server
Loaded: loaded (/home/ektich/.config/systemd/user/eth-server.service; static; vendor preset: enabled)
Active: active (exited) since Tue 2018-05-29 20:50:01 IST; 2min 59s ago
Process: 1579 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 1579 (code=exited, status=0/SUCCESS)
systemctl --user start eth-server.service
Both services are started:
$> systemctl --user start eth-server.service
$> systemctl --user status eth-service
● eth-service.service - eth Service
Loaded: loaded (/home/ektich/.config/systemd/user/eth-service.service; enabled; vendor preset: enabled)
Active: active (exited) since Tue 2018-05-29 20:56:03 IST; 7s ago
Process: 1681 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 1681 (code=exited, status=0/SUCCESS)
$> systemctl --user status eth-server
● eth-server.service - eth Server
Loaded: loaded (/home/ektich/.config/systemd/user/eth-server.service; static; vendor preset: enabled)
Active: active (exited) since Tue 2018-05-29 20:56:03 IST; 12s ago
Process: 1680 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 1680 (code=exited, status=0/SUCCESS)
Commands in following two sections are executed where both services are running:
systemctl --user stop eth-server.service
$> systemctl --user status eth-service
● eth-service.service - eth Service
Loaded: loaded (/home/ektich/.config/systemd/user/eth-service.service; enabled; vendor preset: enabled)
Active: inactive (dead)
$> systemctl --user status eth-server
● eth-server.service - eth Server
Loaded: loaded (/home/ektich/.config/systemd/user/eth-server.service; static; vendor preset: enabled)
Active: inactive (dead)
You can see both services were stopped
systemctl --user stop eth-service
$> systemctl --user stop eth-service
$> systemctl --user status eth-service
● eth-service.service - eth Service
Loaded: loaded (/home/ektich/.config/systemd/user/eth-service.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Tue 2018-05-29 21:05:25 IST; 34s ago
Process: 1800 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 1800 (code=exited, status=0/SUCCESS)
May 29 21:04:23 ektich systemd[479]: Starting eth Service...
May 29 21:04:23 ektich systemd[479]: Started eth Service.
May 29 21:05:25 ektich systemd[479]: Stopped eth Service.
$> systemctl --user status eth-server
● eth-server.service - eth Server
Loaded: loaded (/home/ektich/.config/systemd/user/eth-server.service; static; vendor preset: enabled)
Active: active (exited) since Tue 2018-05-29 21:04:23 IST; 2min 58s ago
Process: 1799 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 1799 (code=exited, status=0/SUCCESS)
May 29 21:04:23 ektich systemd[479]: Starting eth Server...
May 29 21:04:23 ektich systemd[479]: Started eth Server.
You can see that eth-service
has been stopped (Active: inactive (dead)
) but the eth-server
is still "running" (Active: active
)
The status command for etc-service
produced more output compared to other cases, I'm assuming because the "transaction" hasn't finished yet (etc-server
is still running). The extra lines from journalctl
show that as far as systemd is concerned one of the services has been stopped, and the other is still running.
PS according to the systemd homepage it is spelled systemd, not SystemD (just being pedantic)
The systemctl start xxx
generally doesn't revert the effects of a preceding systemctl stop xxx
.
Admins need to learn that in the course of their career one way or another. Good news is that it's really easy to learn, because the feedback is immediate.
One way to revert manual stops and manual starts is to:
systemctl default
But this way is hardly fool-proof, as it stops services which are not enabled, such as services running under various clusters (in particular pacemaker does not even see it as a problem/failure and does not react to it - it treats it as an intended admin action).
What can you do as an author of a service to make it fool-proof? How other programmers handle it is they avoid any dependencies and prefer to pack multiple processes into a single umbrella service. In the default RHEL7 install, there are no service dependencies at all. For example postfix:
$ systemctl status postfix
[...] Tasks: 3
CGroup: /system.slice/postfix.service
├─1288 /usr/libexec/postfix/master -w
├─1290 qmgr -l -t unix -u
└─7340 pickup -l -t unix -u
$ cat /usr/lib/systemd/system/postfix.service
[Unit]
Description=Postfix Mail Transport Agent
After=syslog.target network.target
Conflicts=sendmail.service exim.service
[Service]
Type=forking
PIDFile=/var/spool/postfix/pid/master.pid
EnvironmentFile=-/etc/sysconfig/network
ExecStartPre=-/usr/libexec/postfix/aliasesdb
ExecStartPre=-/usr/libexec/postfix/chroot-update
ExecStart=/usr/sbin/postfix start
ExecReload=/usr/sbin/postfix reload
ExecStop=/usr/sbin/postfix stop
[Install]
WantedBy=multi-user.target