systemd startup tips

August 1, 2020   

Some of my docker containers use NFS mounts that are configured in /etc/fstab. During a reboot, I’ve seen my containers failing to start when docker engine is started before all the mount points are mounted. There is a relatively simple but not too obvious solution to this which involves tweaking systemd rules such as Requires and After.

Let me start out by pointing out that, docker engine is controlled by the following service file


Description=Docker Application Container Engine
Requires=docker.socket docker.socket firewalld.service

By ready through It seems that the Requires sets up logical relationships between various units; which services start together or die together, etc. For example, above configuration states that if docker.socket should be started when the docker.service is started, or conversely if docker.socket is stopped, docker.service should also be stopped (yikes!).

However, the Requires by itself does not prevent docker.service from starting up when docker.socket is started. They are both started simultaneously (or shutdown simultaneously). To make sure that docker service will start only after docker.socket is fully started, we need to add docker.socket in another directive; After. I think you should almost always want to put the dependencies in both Requires and After.

As I said, Requires make your service completely dependent of listed services. If docker.socket is shut down, then docker.service will be shut down also. If you want to let a service running even when the depending service shuts down, then you can use Wants instead. Wants works like Requires, it tries to start those depending service when the service is started. But it doesn’t prevent the service from starting up even those depending services fail to start, or gets shut down. The documentation recommends that Wants/After is usually how most units are hooked together, which makes sense.

However, if I know that certain services are started on boot by something else, like the /etc/fstab mount point case, then all I want to do is to make sure that those mount points gets mounted before docker engine starts up. Then, all I have to do is to simply list it under After and I don’t have to worry about Requires nor Wants.

Then I can do,

Description=Docker Application Container Engine
Requires=docker.socket docker.socket firewalld.service mnt-scratch.mount

This seems to work, and I’ve tested a couple of times by rebooting my machine. Docker was started after my scratch mount point was mounted.

The mnt-scratch.mount here is a systemd unit used to mount the /mnt/scratch specified in my /etc/fstab. But how do I know the name of this unit? I don’t quite understand how systemd translates /etc/fstab to registered units, but you can run the following to dump the current list of known units related to your mount point.

$ systemctl list-units | grep /mnt/scratch
  mnt-scratch.mount                                                                                         loaded    active mounted   /mnt/scratch

This shows that mnt-scratch.mount unit is responsible for mounting /mnt/scratch, which is what I used to update the After= statement with. You can also see the whole dependency tree with systemctl list-dependencies.

Now, if I know that /mnt/scratch is not automatically mounted or started by other systemd services, then I believe I could add it to Wants to make sure that it’s mounted when docker starts up.

Wants=mnt-scratch.mount docker.socket firewalld.service mnt-scratch.mount

Or.. if I really need that mount point for the whole docker engine to operate, then…

Requires=docker.socket mnt-scratch.mount docker.socket firewalld.service mnt-scratch.mount

Be aware that, using Requires means that if /mnt/scratch is unmounted (via systemd?), the whole docker engine will be terminated.. which may or may not be what you want.

comments powered by Disqus