Managing Systemd Tmpfiles

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 /tmp or /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 User and Group in 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
  • %E is /etc
  • %L is /var/log
  • %S is /var/lib
  • %C is /var/cache
  • %T is /tmp
  • %t is /run

Advantages of This Approach

  • Declarative and maintainable: No more hand-crafted init scripts creating folders
  • Automatic cleanup: RuntimeDirectory is 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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top