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:

  1. Starting eth-service should start both eth-server and eth-service.
  2. Starting eth-server should start both eth-server and eth-service.
  3. Stopping eth-server should stop both eth-server and eth-service.
  4. Stopping eth-service should ONLY stop eth-service and leave eth-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