Create and Verify a systemd Mount Unit
A native systemd.mount unit earns its keep once you need boot ordering or dependencies that a plain fstab line cannot express. This guide builds a bind-mount example: create the unit, check its filename matches the destination path, start it, verify the result, and remove it again. The examples target systemd 255.4-1ubuntu8.17 on a Debian-family system.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell, systemd, and a source directory that already exists. The workflow changes the live mount table and writes under /etc/systemd/system, so commands marked sudo require elevated privileges. Do not use the example paths if they contain data that must remain private or untouched.
1. Decide whether you need a unit file
For a human-managed persistent mount, /etc/fstab is generally the preferred configuration. A native .mount file earns its place when tooling owns the configuration, when you need unit dependencies, or when the mount belongs alongside other systemd units. This guide uses a native unit and a bind mount specifically because it needs no formatting or partitioning of a block device.
A bind mount exposes an existing directory at another path; it does not copy anything. Starting the unit changes what applications see below the destination, and stopping it removes that view again. Check the source and destination carefully before starting anything:
$ test -d /srv/project-data && echo source-is-a-directory
source-is-a-directory
$ test ! -e /mnt/project-data && echo destination-is-free
destination-is-free
If either check fails, stop and pick paths you actually have authority over. Do not create the destination just to paper over a path mistake. A mount unit can create a missing mount point for you, but that automatic creation does not make a wrong path safe.
2. Derive the unit filename from Where=
The unit name is not something you get to choose freely. It has to encode the mount point, using systemd's own escaping rules. For /mnt/project-data, ask the installed tool for the exact name rather than guessing:
$ systemd-escape --path --suffix=mount /mnt/project-data
mnt-project\x2ddata.mount
That backslash sequence is part of the filename, so create the file with exactly that name. A unit called project-data.mount can contain perfectly valid settings and still control nothing at all, because its filename does not represent the path in Where=.
Checkpoint
The destination in this guide maps to mnt-project\x2ddata.mount. If you change Where= later, repeat this step rather than editing the old name by guesswork.
3. Write the [Mount] configuration
Create the unit as root. The mandatory settings are What= and Where=; Type=none describes this bind-mount invocation, and Options=bind passes the mount option through to mount(8):
$ sudo install -d -m 0755 /etc/systemd/system
$ sudo tee '/etc/systemd/system/mnt-project\x2ddata.mount' > /dev/null <<'UNIT'
[Unit]
Description=Project data bind mount
[Mount]
What=/srv/project-data
Where=/mnt/project-data
Type=none
Options=bind
DirectoryMode=0755
[Install]
WantedBy=multi-user.target
UNIT
DirectoryMode= only controls directories systemd creates for the mount point and its parents; it does not touch the permissions of the existing source directory. The [Install] section is just enablement metadata, it does not start the unit on its own.
Do not add User= or Group= here. The systemd.mount manual is explicit that they are not useful for mount units, because systemd always invokes mount(8) as UID 0 regardless.
4. Validate before touching the live mount table
Reload the manager's unit files, then ask systemd to parse and verify the unit. Neither of these commands actually mounts anything:
$ sudo systemctl daemon-reload
$ systemd-analyze verify '/etc/systemd/system/mnt-project\x2ddata.mount'
A clean systemd-analyze verify normally prints nothing at all and returns status 0, which is exactly the kind of silent success worth checking explicitly if you are scripting this:
$ printf 'verify status: %s\n' "$?"
verify status: 0
If verification flags a missing source or an invalid option, fix the unit before starting it. Syntax validation cannot prove the source holds the data you expect, and it cannot prove every mount option suits the filesystem underneath.
5. Start the mount and inspect the result
Starting a mount changes system state, so confirm the unit name and destination once more, then start it:
$ sudo systemctl start mnt-project\x2ddata.mount
$ systemctl status --no-pager mnt-project\x2ddata.mount
The status should read Active: active (mounted), though exact timestamps and process details vary. Check the kernel's view too:
$ findmnt --mountpoint /mnt/project-data
TARGET SOURCE FSTYPE OPTIONS
/mnt/project-data /srv/project-data none rw,bind
Column spacing and the full option list may look different on your system. What matters is the target, the source, and a successful exit status. If the start fails, read the unit's journal rather than retrying it over and over:
$ sudo journalctl -u mnt-project\x2ddata.mount -b --no-pager
6. Choose boot behaviour deliberately
You can enable the unit so systemd pulls it in during normal boot:
$ sudo systemctl enable mnt-project\x2ddata.mount
Created symlink ...
The symlink path and exact message vary. Because the unit has WantedBy=multi-user.target, enabling it records a boot relationship, but that is not a substitute for the verification you already did. If this mount must never delay or block boot when it is unavailable, look into nofail and the related dependency behaviour before relying on it. For a mount that should only appear when accessed, an accompanying .automount unit is the better fit.
Network mounts want extra care. systemd classifies mounts by filesystem type, but _netdev can force network-mount ordering when that automatic detection is not enough on its own. Do not copy that option into this local bind-mount example, it has no business there.
7. Stop and remove the example cleanly
Once you are done testing, undo the live change before removing the unit file itself:
$ sudo systemctl disable --now mnt-project\x2ddata.mount
Removed ...
--now stops the mounted unit and disables its boot link in one go. Confirm it is actually gone:
$ findmnt --mountpoint /mnt/project-data
$ printf 'findmnt status: %s\n' "$?"
findmnt status: 1
That non-zero status is expected once nothing is mounted at that path. Only after seeing it should you remove the unit file and reload systemd:
$ sudo rm '/etc/systemd/system/mnt-project\x2ddata.mount'
$ sudo systemctl daemon-reload
Removing the unit file does not delete /srv/project-data, and it does not necessarily remove the empty mount-point directory systemd may have created. Inspect /mnt/project-data before deleting it, and never reach for a broad recursive delete just to tidy up a mount point.
Done means
- Filename derived correctly: generated from the exact
Where=path withsystemd-escape. - Paths checked:
What=andWhere=point to locations you verified first. - Verified before mounting:
systemd-analyze verifyreturned status 0 before the mount was started. - Confirmed live:
systemctl statusandfindmntshowed the active source and destination. - Boot choice deliberate: enabling it was a decision, not an accidental side effect.
- Cleanly retired: the test mount was stopped before its unit file was removed.