Make a Service Wait for a Linux Device with systemd.device
You will identify the systemd unit for a device, make a service depend on it, and verify the relationship without guessing at unit names. This guide uses systemd 255.4-1ubuntu8.17 on the machine where it was written. Allow about fifteen minutes for a simple dependency and longer if you also need a udev rule for a device that systemd does not expose.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Confirm that systemd can see the device
- 2. Understand what a device unit can contain
- 3. Write a small service that requires the device
- 4. Reload the manager and test the dependency
- 5. Decide whether to enable the service
- 6. Use udev properties when appearance should start work
- 7. Diagnose missing or surprising device units
The example uses /dev/loop0, because it is present on this machine. Replace that path with the device you actually need. The service below only checks that the device node is a block device and then exits. It does not mount, format, write to or remove the device.
1. Confirm that systemd can see the device
Device units are generated dynamically. They are not normally files that you create by hand. Start with an ordinary, read-only check:
$ command -v systemctl
/usr/bin/systemctl
$ systemd-escape --path --suffix=device /dev/loop0
dev-loop0.device
$ systemctl show --no-pager --property=Id,ActiveState,Description dev-loop0.device
Id=dev-loop0.device
Description=/dev/loop0
ActiveState=active
The path /dev/loop0 becomes dev-loop0.device. The escaping matters for names such as /dev/disk/by-id/...; do not make up the hyphens yourself. If the device is not present, systemctl show may report that the unit cannot be found or may show an inactive state.
Checkpoint: keep the output of systemd-escape. The generated name is the value you will use in Requires= and After=.
2. Understand what a device unit can contain
A .device unit has no [Device] section and no device-specific options. Its configuration uses the common [Unit] and [Install] sections described by systemd.unit. In practice, the useful job here is to express a dependency from another unit to the device.
Systemd normally creates device units for kernel devices carrying the systemd udev tag. Block and network devices are included by default, along with some other devices. The unit becomes active when the corresponding device is considered plugged. There are no default dependencies for device units, so add ordering explicitly where your service needs it.
A device unit is not the same thing as a persistent device file. It describes the device event as seen through sysfs and udev. A service can therefore wait for the device unit even when the device was absent when the service was first installed.
3. Write a small service that requires the device
This step changes persistent system configuration and requires elevated privileges. Choose a real service name in place of device-check.service. The example is deliberately harmless:
[Unit]
Description=Check that loop0 is available
Requires=dev-loop0.device
After=dev-loop0.device
[Service]
Type=oneshot
ExecStart=/usr/bin/test -b /dev/loop0
RemainAfterExit=yes
Save that content as /etc/systemd/system/device-check.service with an editor run through sudo. Requires= means the service cannot be started successfully without the device unit. After= controls ordering; it does not itself pull the device into the transaction and it does not make a missing device appear. Using both expresses the usual intent clearly.
Do not use ExecStart=/bin/sh -c ... for this simple test. A direct executable avoids an extra shell and makes quoting errors less likely. For a real service, replace the test with the program that needs the device, and keep its device access narrow.
4. Reload the manager and test the dependency
After creating or changing a unit file, ask systemd to reread unit files. This is an elevated, non-destructive manager operation:
$ sudo systemctl daemon-reload
$ sudo systemctl start device-check.service
$ systemctl show --no-pager --property=Id,ActiveState,SubState,After device-check.service
Id=device-check.service
ActiveState=active
SubState=exited
The exact After= line can contain more units than the abbreviated output shown here. Check the result and the service log if the start fails:
$ systemctl status --no-pager device-check.service
$ journalctl -u device-check.service -n 20 --no-pager
An active, exited oneshot means the command completed successfully and systemd retained the service's active state because of RemainAfterExit=yes. It does not prove that a larger application can use the device. Test the application's own operation separately.
Checkpoint: verify the dependency rather than trusting the unit file text:
$ systemctl show --no-pager --property=Requires,After device-check.service
Requires=dev-loop0.device ...
After=dev-loop0.device ...
5. Decide whether to enable the service
The example can be started manually, which is useful for a first test. It is not enabled at boot because the unit has no [Install] section. If the real service should start whenever a normal boot target starts, add this section to the service:
[Install]
WantedBy=multi-user.target
Then run the following as an administrator:
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now device-check.service
$ systemctl is-enabled device-check.service
enabled
Enabling creates a symlink in systemd's unit configuration hierarchy. It does not turn a device on and it does not make the device permanent. If this was only a test, undo it with sudo systemctl disable --now device-check.service. To remove the test unit completely, delete the file you created and run sudo systemctl daemon-reload; deletion is irreversible, so preserve a copy if the file contains real service configuration.
6. Use udev properties when appearance should start work
Sometimes the desired relationship is the reverse: a particular device event should want a service. A udev rule can add the systemd tag and set SYSTEMD_WANTS=. The property is read by the system service manager; SYSTEMD_USER_WANTS= is for a user manager instance.
ACTION=="add", SUBSYSTEM=="block", KERNEL=="loop0", TAG+="systemd", ENV{SYSTEMD_WANTS}+="device-check.service"
This is a security-sensitive configuration change. Match stable properties such as an approved device identity rather than a broad kernel name when hardware can change. Put a rule under /etc/udev/rules.d/, validate its syntax with your distribution's udev tools, then reload rules and trigger only the intended device according to your operating procedure. Do not blindly run a broad trigger on a production host.
There is a timing trap here: systemd acts on SYSTEMD_WANTS when the device first becomes active. Adding the property to an already active device does not retrospectively start the wanted unit. A rule can set SYSTEMD_READY=0 while a device is incomplete, then set it to 1 when a changed event marks it ready. Do not use this mechanism unless you understand the device's udev event sequence.
7. Diagnose missing or surprising device units
If a device unit is absent, first check the three layers separately:
- Check that the kernel device exists with
ls -l /dev/...and inspect its sysfs path. - Check that systemd-udevd is running with
systemctl is-active systemd-udevd.service. - Check the generated name with
systemd-escape --path --suffix=device /dev/....
If systemd-udevd.service is not running, systemd has no device units available from udev. This is common in a typical container. A service dependency cannot repair that missing event source. Check the container's device permissions and manager arrangement instead of adding more unit dependencies.
If a service starts before the hardware is usable, distinguish presence from readiness. A device unit can tell systemd that the udev device is plugged, but it cannot validate an application's protocol, filesystem, firmware state or remote storage availability. Put that readiness check in the service or in a narrowly scoped helper, and make failure visible rather than hiding it with an unconditional sleep.
Device units are reloaded when their device produces a udev changed event. Other units can use ReloadPropagatedFrom= when they must react to that event. This is an advanced relationship, not a substitute for Requires= or After=.
Done means
systemd-escapeproduced the device unit name from the actual device path.- The dependent service uses
Requires=for presence andAfter=for ordering. daemon-reloadwas followed by a real start test and a dependency check.- Boot enabling was treated as an explicit choice and can be undone with
disable --now. - udev rules, readiness and container limitations were considered before blaming systemd.device.