Path units are useful when work should start only after a file, directory, or marker path changes. They fit jobs such as reloading an application after a config file update, processing files dropped into a queue directory, or reacting when a flag file appears.
In systemd, a *.path unit monitors one or more absolute paths and activates another unit when the watch condition is met. The upstream systemd.path documentation defines PathExists=, PathChanged=, PathModified=, and DirectoryNotEmpty= as the main triggers, and a path unit starts the matching service with the same basename by default unless Unit= points to a different unit. The example below watches /etc/path-demo/input.txt and starts a small oneshot service named config-watch.service whenever that file changes.
Path units use the kernel inotify mechanism, so they are best for local filesystems and do not reliably detect remote changes made on NFS by another machine. The path unit itself stays in active (waiting) while the triggered oneshot service usually returns to inactive (dead) after a successful run, and fast trigger loops can eventually hit the unit start limits. For per-user watches, place the units under /~/.config/systemd/user and use systemctl --user instead of the system manager commands shown here.
$ sudo mkdir -p /etc/path-demo $ sudo touch /etc/path-demo/input.txt
The watched path should exist before normal use so the first change is easy to verify. This example uses a regular file because it makes the PathChanged= trigger easy to demonstrate safely.
[Unit] Description=Record changes to /etc/path-demo/input.txt [Service] Type=oneshot ExecStart=/bin/sh -c 'date --iso-8601=seconds >> /var/log/config-watch-trigger.log'
The matching service can run any safe action, such as reloading an application, copying a file, or starting a queue processor. A oneshot service works well when the trigger should perform a short task and exit.
[Unit] Description=Watch /etc/path-demo/input.txt for changes [Path] PathChanged=/etc/path-demo/input.txt Unit=config-watch.service [Install] WantedBy=paths.target
PathChanged= triggers after the file is closed following a write. Use PathExists= when the service should run as soon as a file or directory exists, PathModified= when every write matters, and DirectoryNotEmpty= for queue-style directories. All watched paths must be absolute.
The upstream systemd.special documentation recommends WantedBy=paths.target for path units that should be active after boot.
$ sudo systemd-analyze verify /etc/systemd/system/config-watch.service /etc/systemd/system/config-watch.path
No output means systemd accepted the syntax and the unit relationship.
$ sudo systemctl daemon-reload
This rereads the unit definitions from disk before the new path unit can be enabled or started.
$ sudo systemctl enable --now config-watch.path Created symlink /etc/systemd/system/paths.target.wants/config-watch.path -> /etc/systemd/system/config-watch.path.
The *.path unit is the object to enable. The triggered *.service unit is usually static and is pulled in automatically when the watch condition is met.
$ systemctl status --no-pager --full config-watch.path | head -n 5
● config-watch.path - Watch /etc/path-demo/input.txt for changes
Loaded: loaded (/etc/systemd/system/config-watch.path; enabled; preset: enabled)
Active: active (waiting) since Mon 2026-04-13 09:00:05 UTC; 9ms ago
Triggers: ● config-watch.service
The active (waiting) state is the success condition for the path unit itself. It means systemd is monitoring the configured path and will start the service on the next matching change.
$ sudo sh -c 'echo "# change $(date --iso-8601=seconds)" >> /etc/path-demo/input.txt'
$ cat /var/log/config-watch-trigger.log
2026-04-13T09:00:31+00:00
$ systemctl status --no-pager --full config-watch.service | head -n 4
○ config-watch.service - Record changes to /etc/path-demo/input.txt
Loaded: loaded (/etc/systemd/system/config-watch.service; static)
Active: inactive (dead) since Mon 2026-04-13 09:00:31 UTC; 388ms ago
TriggeredBy: ● config-watch.path
The log file shows that the service ran, while the inactive (dead) state is normal for a successful oneshot service after it exits. If the log stays empty, inspect journalctl -u config-watch.path -u config-watch.service for the real error.