Auto-mounting network file systems with systemd

If you're new to systemd you're probably thinking that the title sounds like the sort of thing that should be easy, and you're right! If you're used to systemd, you'll already know that it's probably not easy, and you're also right!


On the surface, you should be able to just add your entries to /etc/fstab as you've been doing up until now and if that's working for you: stick to it!

If, however, you either a) are having problems with mounts being attempted before your network is up (common with WiFi systems) or b) want to do everything the full systemd way, then read on!

The Mount File

Systemd includes a helpful mount unit type, handled as a .mount unit file (just like .service etc). As of the time of writing, this file actually just gets 'executed' through the usual mount command anyway, but let's have a look at what a mount file looks like.

Check out James Oguya's excellent post on this topic for the full details.

[Unit]
Description = Mount NFS Share

[Mount]
What=172.24.0.5:/srv/backups
Where=/mnt/backups
Type=nfs
Options=defaults
# Uncomment the below if your server is real slow
# TimeoutSec=10

[Install]
WantedBy=multi-user.target

and that's it! Sort of.

Put this file in /etc/systemd/system like your other system units, but be warned: the file must be named exactly for its target mount point. In the example above, the file would be /etc/systemd/system/mnt-backups.mount.

Don't ask me why, I don't know either.

Then run systemctl daemon-reload and systemctl start mnt-backups.mount to mount your filesystem. Now to mount at boot, you would think you can just systemctl enable ... but unfortunately it's not that simple.

Putting a boot in it

Now if you just run systemctl enable ... with your unit as it stands, it will almost certainly fail since systemd won't know to wait for the network before it actually runs the mount.

You can control this using the After= option in the [Unit] section:

[Unit]
Description = Mount Server Private Directory
After=## this bit here ##

[Mount]
...

Now your first thought will be to use the "special" remote-fs.target here and this might work in simple setups. If your system is using Network Manager however, the accepted wisdom seems to be to use After=NetworkManager-wait-online.service as your dependency. This will usually work for wired connections, so try this first.

There's a bit of a caveat here though:

NetworkManager doesn't actually wait

If you check the /usr/lib/systemd/system/NetworkManager-wait-online.service file yourself, you'll notice the service is just using nm-online to wait for the network. However, if you check the arguments being used, you'll see that using the -s flag here only waits for NetworkManager itself to be ready, not the actual connection to be connected and ready. While you can simply remove the -s flag here (see notes below), I find it better to create a new service.

Creating a service that actually waits

In my case, I created the following file at /etc/systemd/system/network-online.service:

[Unit]
Description=Wait until NM actually online
Requires=NetworkManager-wait-online.service
After=NetworkManager-wait-online.service

[Service]
Type=oneshot
ExecStart=/usr/bin/nm-online -q --timeout=120
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

This will run after the existing NetworkManager-wait-online.service but will only successfully start (and stay active) once Network Manager has actually connected to the network.

Run systemctl daemon-reload and systemctl enable --now network-online.service to confirm your service is working and enable the check to run at boot

Adding this to your mount

Now you have this service, you can slightly tweak your existing .mount file with the following lines in the [Unit] section:

Requires=network-online.service
After=network-online.service

Now your mount won't be run until NetworkManager reports your network as actually connected.

Putting it together

You can actually use this new network-online.service as a After= criteria on any other services you have that should wait until the network is actually connected, instead of just having the stack up.

Notes

What about if I'm not using NetworkManager?

Fair point actually. The overall principles at work remain the same: create a service with a command that will only return a success code when the network is available and have your mounts depend on that. You'll just have to find the right system service to use as an After= substitute for NetworkManager-wait-online.service and find a command you can use to replace nm-online.

What about removing the -s flag in the builtin unit?

So this is a valid approach with two important caveats:

  • You might break existing stuff. In my non-rigorous testing, boot behaviour actually got worse when I made this change and there were a few services that seemed to have trouble starting/waiting for the modified service.
  • DO NOT modify the builtin service file. If you're on a vaguely recent release, you should be able to create a /etc/systemd/system/NetworkManager-wait-online.service.d/ directory and put .conf files in there to override the built-in one. Check the docs or the better unofficial docs

Can't you do this in /etc/fstab?

Yes and no, in my experience. Yes, /etc/fstab is both the easiest and most Linux-y way of doing mounts, but it's also unintuitive, error-prone and doesn't integrate well with things like NetworkManager/netctl/whatever. Personally, I'd prefer to whip up an easy .mount file then try and decipher whatever x-systemd.automount means, and which column of this plaintext file it should be in.

Why not use automount?

This is the big one: the answer to the vast majority of problems with NetworkManager/systemd/etc and mount delay is to use "automounts" which are essentially lazy filesystem mounts, that are auto-mounted the first time they're accessed. For the most part, these do solve a few of the problems from above. That being said, if you want your boot to actually wait for the filesystem rather than leave it until first access, you will need to use this method instead.