in this post we will look at how systemd handles dependencies and how they can be used to increase the robustness of the system, and also discuss potential pitfalls.

For the existing services all of the examples should be put in /etc/systemd/system/<service you change>.d/<override>.conf, as this way you will only override the things you change in unit, instead of editing whole unit file in /lib (and having your changes overridden on package upgrade).

Unless mentioned otherwise the dependencies are specified in [Unit] section

Types of dependencies

All of them accept space separated list of units.

Wants

Unit "wanting" dependency makes systemd try to run the specified dependency when you start the unit. Note that doesn't mean the dependency needs to be successful, if wanted dependency fails the unit will still run.

It also doesn't impose order, just the fact it must be running, which makes it probably most confusing one of the list.

WantedBy

Reverse of Wants, with caveat that it should be added in the [Install] section as opposed to '[Unit]` section

Requires

Is the stronger version of Wants. Failure to run one will fail your unit but it still doesn't imply After. It will also make your unit stop if the dependency is stopped.

Note that there is a bit of race condition here, as just using Require won't impose the order and won't make your unit wait for the required unit to start (and possibly fail) so without explicit After it might still run if the dependent unit failed.

For that reason most of the unit files have both Requires and After on units they depend on

Requisite

It's Requires that doesn't automatically start required services and fails if they are not running at the moment

Upholds

It is continuous version of Wants. Any unit specified there will be restarted any time it is found not running.

Conflicts

Two dependencies that conflict eachother will not error out when trying to run any of them. That also doesn't imply ordering.

Instead, the old one will be stopped and the new one will be started at the same time.

RequiresMountsFor

Space separated list of filesystem paths needed for unit to work. Any mounts on that path will be added as both Requires and After dependency.

This is probably most useful in context of databases (especially if said database tries to init empty DB when faced with empty dir), as you can use it to easily make "do not start DB if volume it is using didn't mount correctly" dependency with it.

Mounts with noauto will not be included in this which is a bit of a trap.

Before/After

Configure ordering between the units. Adding that dependency will make systemd fully start one unit before activating one dependant on it, instead of trying to do it all at once, and reverse of that on shutdown

Devices have no working Before as they are created by hotplug events, not activated

PropagatesReloadTo=/ReloadPropagatedFrom=

As name implies, reload of this unit will propagate reload to other units. The link is in one direction

PropagatesStopTo=/StopPropagatedFrom=

As name implies, stop of this unit will propagate stop to other units. The link is in one direction

BindsTo

On top of dependencies that Requires would provide, BindsTo make it so unit will be stopped if the bound unit is stopped. In most cases you also want to add After to the same dependency to make sure the unit is also started after the service it is bound to is activated. This is pretty hard dependency as even if service will be stopped unexpectedly (say the other one crashes), the bound service will also be stopped

Upholds

Literally "hold that service up". Services defined in Upholds will be kept running (and restarted in case they fail) as long as service that defined it is kept running. This only happens for starting the service, the services won't be stopped if you stop the unit that specified Upholds.

It might be a good way to keep services your service depends on running without having to modify each one with Restart/RestartSec however one caveat is that this can be nontrivial to debug for ops people that are unfamiliart with gritty details of systemd so use it with care. This is probably a reason why it is very rarely used in distros.

PartOf

PartOf "leeches" any stop and restart event of the unit it is part of. Main usage is making sure few units that always should work together will also be restarted together

StopWhenUnneeded=

Normally systemd will not stop units unless they collide with other units.

StopWhenUnneeded=true will actively garbage collect services that are not depended on by anything else. Can be useful if you have service that's only useful to other services, for example a lot of distros have bluetooth service using that option.

Examples of usage

Start database only if the filesystem it uses is mounted

There are 2 ways of doing it. The "generic" way would be just pointing out the unit DB needs to start:

1[Unit]
2Requires=var-lib.mount
3After=var-lib.mount

The more specific way would be using RequiresMounsFor:

1[Unit]
2RequiresMountsFor=/var/lib/mysql /etc/mysql/

And some packages modern distros do exactly that. From Debian:

1ᛯ cat /lib/systemd/system/postgresql@.service |grep Mounts
2RequiresMountsFor=/etc/postgresql/%I /var/lib/postgresql/%I

(sadly mariadb package doesn't do that, at least at this moment)

Keeping flaky service you depend on running

The standard way would be to just add restart/restart sec to the service that is flaky

/etc/systemd/system/flaky.service:

1...
2[Service]
3ExecStart=/usr/bin/flaky-service
4Restart=always,
5RestartSec=10

and then using normal dependencies on service that is depending on it

/etc/systemd/system/our.service:

1[Unit]
2BindsTo=flaky.service
3After=flaky.service

Alternative is to just specify Upholds

/etc/systemd/system/our.service:

1[Unit]
2Upholds=flaky.service

but that can be annoying to debug if someone tries to stop the flaky.service manually and it just gets instantly restarted with apparently no reason. Being explicit here is usually a better way.

Running service when the given device is mounted.

Let's assume for a second we want to run a backup script or a sync daemon every time external drive is connected.

First, we set up our service

/etc/systemd/system/run-sync.service:

 1[Unit]
 2Description=run sync service if external drive is connected
 3# We want it after mount is mounted
 4After=mnt-data.mount
 5# We want it to stop when the mount is unmounted
 6BindsTo=mnt-data.mount
 7[Service]
 8ExecStart=/usr/local/bin/sync-script
 9... # whatever other options the service needs
10
11[Install]
12WantedBy=multi-user.target 

then, we modify mount to trigger it:

/etc/systemd/system/mnt-data.mount.d/run-sync.conf:

1[Unit]
2Wants=run-backup.service

and reload systemd via systemd-daemon reload

Result:

 1 (!) [15:09:39]:/lib/systemd/system32☠ mount /mnt/data
 2 (!) [15:09:42]:/lib/systemd/system☠ systemctl status run-sync
 3● run-sync.service - run sync after mount
 4     Loaded: loaded (/lib/systemd/system/run-sync.service; enabled; vendor preset: enabled)
 5     Active: active (running) since Sun 2022-02-27 15:09:42 CET; 2s ago
 6   Main PID: 189014 (sync-script)
 7      Tasks: 2 (limit: 38330)
 8     Memory: 576.0K
 9        CPU: 5ms
10     CGroup: /system.slice/run-sync.service
11             ├─189014 /bin/bash /usr/local/bin/sync-script
12             └─189016 sleep 60m
13
14Feb 27 15:09:42 hydra systemd[1]: Started run sync after mount.
15 
16 (!) [15:09:47]:/lib/systemd/system☠ umount /mnt/data
17 (!) [15:09:49]:/lib/systemd/system☠ st run-sync
18○ run-sync.service - run sync after mount
19     Loaded: loaded (/lib/systemd/system/run-sync.service; enabled; vendor preset: enabled)
20     Active: inactive (dead) since Sun 2022-02-27 15:09:49 CET; 855ms ago
21    Process: 189014 ExecStart=/usr/local/bin/sync-script (code=killed, signal=TERM)
22   Main PID: 189014 (code=killed, signal=TERM)
23        CPU: 5ms

service gets stopped when the mount is unmounted.

Caveat with mounts.

mount is run independently of systemd that can then only take action on that. Which means if your service have actively open files, umount /mnt/data will fail.

Workaround for it is to tell systemd to stop the mount via systemctl stop /mnt/music, or to have script itself only be active from time to time (say syncing every hour), then hope you won't hit that. Mount is just not integrated with systemd.