System administrators are all too familiar with how annoying sometimes it can be to encounter errors like “No such file or directory”, “Permission denied” and others. These problems are often related to the systemd tmpfiles and directories for logs, caches and state. With systemd, there’s a powerful way to manage these entityes declaratively — using unit configuration options like RuntimeDirectory, StateDirectory, CacheDirectory, LogsDirectory, and ConfigurationDirectory*. These are part of the modern systemd sandboxing and resource management features, designed to simplify service file management.
In this article, we’ll explore these directives in-depth, with real examples and practical guidance.
The Problem: Where Should Services Store Their Data?
Traditionally, daemons would write:
- Temporary files to
/tmpor/var/tmp - Runtime files like sockets or PID files to
/run - Logs to
/var/log - State data to
/var/lib - Cache to
/var/cache
With systemd, there’s a better way. Instead of hardcoding paths inside your application or relying on pre-created folders, you can let systemd handle this.
RuntimeDirectory — Ephemeral Storage
RuntimeDirectory creates a subdirectory in /run** for your service. It is automatically cleaned up when the service stops. You can even create multiple directories or manage ephemeral symlinks to those directories as well. Convinient, right?
Example:
[Service]
RuntimeDirectory=redis
This creates /run/redis when the service starts and removes it when the service stops. You can reference it in the ExecStart= using %t/redis (%t is /run).
Example usage:
[Service]
RuntimeDirectory=redis
ExecStart=/usr/bin/redis --pidfile=%t/redis/redis.pid
StateDirectory — Persistent Service Data
Use StateDirectory for storing long-lived data like databases or runtime configuration.
[Service]
StateDirectory=redis
This creates /var/lib/redis with correct permissions. Use %S for /var/lib:
ExecStart=/usr/bin/redis --dir=%S/redis
CacheDirectory — Non-Essential, Regenerable Data
[Service]
CacheDirectory=nginx
Creates /var/cache/nginx where nginx can store cached http responses from the backend. Then, it has to be additionly configured at the nginx level.
LogsDirectory — Log Files
Instead of writing directly to /var/log, define:
[Service]
LogsDirectory=redis
Which creates /var/log/redis. Journald will still collect logs, but you can write structured logs or rotation-sensitive data here.
ConfigurationDirectory — Runtime Configuration
[Service]
ConfigurationDirectory=mycustomservice
Creates /etc/mycustomservice. You may preload default configs or allow the service to create and edit them.
Permissions and Ownership
All these directives automatically:
- Create the directories with safe defaults (0755, root:root)
- Adjust ownership if you also use
UserandGroupin your service file - Clean up ephemeral directories
Example:
[Service]
User=kafka
Group=kafka
RuntimeDirectory=kafka
StateDirectory=kafka
LogsDirectory=kafka
Now /run/kafka, /var/lib/kafka, and /var/log/kafka will all be owned by kafka user.
Real-World Example: Custom Web Server
[Unit]
Description=My Custom HTTP Service
After=network.target
[Service]
User=webapp
Group=webapp
ExecStart=/usr/local/bin/myhttpd --config %E/webapp/httpd.conf --logdir %L/webapp
RuntimeDirectory=webapp
StateDirectory=webapp
LogsDirectory=webapp
ConfigurationDirectory=webapp
[Install]
WantedBy=multi-user.target
%Eis/etc%Lis/var/log%Sis/var/lib%Cis/var/cache%Tis/tmp%tis/run
Advantages of This Approach
- Declarative and maintainable: No more hand-crafted init scripts creating folders
- Automatic cleanup:
RuntimeDirectoryis removed on service stop - Consistency: System-wide conventions, good for packaging
- Security: Proper ownership and permissions
Final thoughts
If you’re writing or maintaining systemd service units, make use of these directory options. They lead to clearer, safer, and more maintainable setups — and help avoid those annoying “failed to open file” errors at the service’s start time.
* Official documentation can be found here
** In case you need more flexibility than systemd tmpfiles related options can provide – consider usng tmpfiles.d