systemd-tmpfiles: Provision and Clean Shared Linux State Safely
Use a narrowly scoped tmpfiles.d rule for filesystem lifecycles that outlive one service unit, preview age-based cleanup, and avoid broad or destructive sweeps.
systemd-tmpfiles creates, removes, and ages filesystem entries from declarative tmpfiles.d rules. It is useful for temporary or shared paths whose lifetime is not naturally tied to a single unit. It is not automatically the best way to create every service directory: systemd recommends unit properties such as RuntimeDirectory=, StateDirectory=, and CacheDirectory= to declare directories owned by one service. Choose the property whose documented persistence and cleanup behavior matches the data; runtime paths and persistent state/cache paths do not all share one lifecycle.
The examples below follow the upstream systemd v262 manuals. Distributions often ship an earlier systemd build or backport selected features, so check the installed tmpfiles.d(5) and systemd-tmpfiles(8) pages before relying on an option or rule type.
Put the rule at the right lifecycle boundary
Use tmpfiles.d for a path that is independently managed, such as a shared artifact cache consumed by more than one unit, or for file setup that needs more detailed cleanup than a unit directory property provides. If one service owns the path, prefer its unit’s directory properties first; they centralize configuration, and RuntimeDirectory= in particular has a unit-bound lifetime, while state/cache properties have separate persistence semantics. Do not add a second mechanism that races the unit or changes ownership unexpectedly.
For a shared cache, create a local file such as /etc/tmpfiles.d/acme-artifact-cache.conf:
# Type Path Mode User Group Age Argument
d /var/cache/acme-artifact-cache 0750 acme-cache acme-cache 30d -
The d rule creates the directory if needed, applies the declared access settings, and makes entries eligible for age-based cleanup. The 30-day age is an example policy, not a guarantee that every item expires exactly 30 days after creation: systemd evaluates filesystem timestamps, and a more recently accessed or modified entry can remain. Cleanup is not application-aware. Do not point an age rule at user documents, durable service state, or a tree where “old” does not mean “safe to delete.”
Before applying the rule, make sure the intended account and group already exist and that the path is dedicated to this cache. Inspect the exact target and its path components before creating it. If the final directory already exists, stat and namei show its current type, ownership, mode, and parent permissions. If it is absent, inspect the nearest existing parent and use namei on the full target to confirm which component is missing:
getent passwd acme-cache
getent group acme-cache
if [ -e /var/cache/acme-artifact-cache ]; then
stat /var/cache/acme-artifact-cache
else
stat /var/cache
fi
namei -l /var/cache/acme-artifact-cache
If the parent path, owner, or group differs from the plan, stop and resolve that discrepancy rather than broadening the rule. d is narrower than the D, R, or r types that remove more data; this example deliberately avoids recursive removal rules.
Understand precedence before adding a local file
Systemd searches tmpfiles configuration directories including /etc/tmpfiles.d, /run/tmpfiles.d, and /usr/lib/tmpfiles.d. A same-named local file can override a vendor file, while /run overrides /usr/lib; files are also ordered lexicographically across directories. Two rules that target the same path can therefore conflict even when they live in different directories.
Use a package-specific filename, inspect the vendor rules for the target tree, and look for duplicate or parent/child paths before rollout. /etc is the administrator’s place for local configuration; packages normally install their own rules under /usr/lib. Do not copy a vendor file wholesale merely to change one line, and do not assume that a later filename wins for a duplicate path.
Preview only the rule you intend to run
Because the command accepts an explicit configuration path, you can constrain a review to this file instead of running every system rule:
sudo systemd-tmpfiles --dry-run --create /etc/tmpfiles.d/acme-artifact-cache.conf
sudo systemd-tmpfiles --create /etc/tmpfiles.d/acme-artifact-cache.conf
sudo systemd-tmpfiles --dry-run --clean /etc/tmpfiles.d/acme-artifact-cache.conf
Review the proposed directory creation, mode, and ownership before the --create step. The cleanup dry run prints what the rule would remove without changing the filesystem. If those candidates are exactly the intended cache contents, run the matching --clean command during the approved window:
sudo systemd-tmpfiles --clean /etc/tmpfiles.d/acme-artifact-cache.conf
Keep the file argument in place: invoking systemd-tmpfiles without positional paths processes the discovered configuration set, which is a much broader operation. Long --dry-run is documented since systemd v256; on older releases, verify availability in the installed manual rather than substituting an unfamiliar option. Do not use --remove or --purge as a cleanup shortcut: those modes have different removal semantics and are not a preview.
On systems using the standard systemd-tmpfiles-clean.timer, age-based rules in the system configuration are also eligible during its scheduled cleanup. Check the local timer schedule and account for that automatic run before installing a rule; the manual preview is not a standing approval for future deletions. After provisioning, inspect the path with stat and namei -l, then run the service’s own health check. A successful tmpfiles command proves only that its configured filesystem operations completed; it does not prove the application can write the cache, that cached objects remain valid, or that a 30-day policy is safe for the workload. Observe cleanup candidates and disk usage before expanding the path or tightening the age.
Related:
- systemd-sysusers: Provision Linux Service Accounts Declaratively
- Fixing ‘Disk Full’ on Linux When df Shows Space Available
Sources: