Temporary files and directories are a basic function of almost all operating systems. They store data generated by the operating system and different applications running on it. If not regularly cleaned, these files can take up a large amount of storage space. To avoid disk space issues and possible data leaks, the best practice is to clean up these files regularly. That is where systemd-tmpfiles comes in.

The systemd-tmpfiles services is a configurable tool for managing temporary files and directories. It creates, removes, and cleans up volatile and temporary data that is stored on the system. This article will help you to understand how systemd-tmpfiles functions as it automatically creates, removes, or cleans the temporary files in our system.

systemd-tmpfiles Unit Files

systemd-tmpfiles is made up of several different unit files. There are four services units which serve different functions, and a single timer unit to schedule cleaning.

  • systemd-tmpfiles-setup-dev.service - Creates Static Device Nodes in /dev
  • systemd-tmpfiles-setup.service - Creates Volatile Files and Directories
  • systemd-tmpfiles-clean.service - Cleanup of Temporary Directories
  • systemd-tmpfiles-clean.timer - Timer unit to trigger systemd-tmpfiles-clean.service

The last three unit files above also have user-space counterparts. They can run the service as a non-privileged user. They serve the same purposes, but have some minor differences. Besides the description, the other main differences are:

  • The system service unit and the user service unit execute at different targets. The system unit executes after local-fs.target and time-set.target, but before shutdown.target. Where the user target starts before basic.target and shutdown.target.
  • The ExecStart line adds the --user designation in the command to specify it is running in the user-space.

Let's take a closer look at each of these services and timer units. Remember, the functions are the same whether is a system service unit or a user service unit.

Create Static Device Nodes - Systemd-tmpfiles-setup-dev.service

This part of the service is rarely interacted with unless you are working on specific hardware or device driver development. Therefore, we will just touch on the subject and move on.

Every device that interacts with the operating system must have a software name. This is usually referred to as a device special file, or device node. These device nodes are stored in the /dev directory. The systemd-tmpfiles-setup-dev.service creates some of these device special files, or device nodes, on boot. It does this by specifying the --prefix=/dev option when executing systemd-tmpfiles.

ExecStart=/usr/bin/systemd-tmpfiles --prefix=/dev --create --boot

Creating and Removing Temp Files at Boot - Systemd-tmpfiles-setup.service

When a system running systemd boots, the systemd-tmpfiles-setup is one of the first units started. This unit runs a command which creates and removes the directories and files designated in it's configuration.

ExecStart=/usr/bin/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev

The --boot ensures that it executes actions that are marked to be run at boot. This service explicitly excludes anything with the /dev prefix.

Scheduled Cleanup of Temporary Files and Directories - systemd-tmpfiles-clean.service

The systemd-tmpfiles-clean.service is the service that runs at scheduled times. A timer unit of the same name controls when it executes. It executes systemd-tmpfiles with only the --clean option.

ExecStart=systemd-tmpfiles --clean

When the --clean option is passed, all files and directories with an age field specified will be cleaned.

Timer Unit to Trigger Scheduled Cleanups - systemd-tmpfiles-clean.timer

We discussed creating systemd timer units in a past article called "Using systemd Timer Units to Schedule Jobs". A systemd timer unit is a configuration file that is used to activate a service unit of the same name. Likewise, the systemd-tmpfiles-clean.timer unit triggers the systemd-tmpfiles-clean.service unit as specified intervals. The default configuration schedules the timer to trigger at 15 minutes after boot, and once a day while the system is running.

[Timer]
OnBootSec=15min
OnUnitActiveSec=1d

OnBootSec - Defines the time relative to when the machine boooted.
OnUnitActiveSec - Defines the timer relative to the last service unit activation.

For more in-depth information about timer units and their starting point, see the resources section below.

You can check the time left on the timer. For example, the below output shows that the systemd-tmpfiles-clean.timer will trigger again in 18 hours.

Checking time left on systemd-tmpfiles timer unit

systemd-tmpfiles Configuration Files

The configuration for systemd-tmpfiles is made up of many different files. A single file is typically generated for each service or application. The systemd-tmpfiles checks the following locations for configuration files:

  • /etc/tmpfiles.d/*.conf
  • /run/tmpfiles.d/*.conf
  • /usr/lib/tmpfiles.d/*.conf

Files stored in the above directories are used to set the configuration for the systemd-tmpfiles. Files placed in the /etc/tmpfiles.d/ directory have the highest priority. The /run/tmpfiles.d/ directory has the second highest priority, with /usr/lib/tempfiles.d having the lowest priority. If a file with the same name was placed in all three directories, the file in /etc/tmpfiles.d/ would take precedence over the others.

Additionally, user specific configurations can be stored in:

  • ~/.config/user-tmpfiles.d/*.conf
  • $XDG_RUNTIME_DIR/user-tmpfiles.d/*.conf
  • ~/.local/share/user-tmpfiles.d/*.conf

Configuration Syntax

All of the systemd-tmpfiles follow the same syntax. The format is one line per path (file or directory) which consists of seven fields.

  1. Type - Consists of a single letter and optional special character (!, -, or +) that tells systemd-tmpfiles which action to take.
  2. Path - File system path specification for the target file or directory.
  3. Mode - The mode or permissions of target file in binary representation.
  4. User - The user to use for target file
  5. Group - The group to use for target file
  6. Age - Date field used to determine if a file should be acted upon.
  7. Argument - Serves different functions depending on what type is set.

This is of course a simplified explanation of each field. For more in-depth information see the resources section below. We will discuss some of the more common types, age uses and arguments below in the examples section.

Here is an example configuration file placed in ~/.config/tmpfiles.d/:

[savona@putor tmpfiles.d]$ cat myscript.conf 
d	/tmp/myscript	700	savona	savona	1h

Manually Running systemd-tmpfiles Configurations

All configuration files (system or user) are checked when the service units run. However, you can run all or one of the configurations manually with the systemd-tmpfiles binary.

To manually run all system configurations invoke the systemd-tmpfiles command with the desired action(s). For example to run a remove:

sudo systemd-tmpfiles --remove

or

sudo systemd-tmpfiles --clean --remove --create

NOTE: You will have to use sudo or elevate to root to run system configurations.

You can run systemd-tmpfiles against a specific configuration file by passing it as an argument to the end of the command. For example, this would run the dns.conf file only:

$ sudo systemd-tmpfiles --remove /usr/lib/tmpfiles.d/dnf.conf

To run user-space configurations you will need to add the --user option like so:

systemd-tmpfiles --user --remove ~/.config/user-tmpfiles.d/delete-chrome-cache.conf 

Examples of Managing Temporary Files with systemd-tmpfiles

Now that we know all the parts involved in systemd-tmpfiles, let's see some examples. Below are examples of system level uses, followed by user-space specific examples.

Real World System Services Examples

Example 1: Create Directory and Set It's Permissions at Boot

OpenSSH requires the directory /var/empty/sshd to be present, owned by root, and not world writable. If it is not, the service will fail to start with the following error.

sshd[12861]: Missing privilege separation directory: /var/empty/sshd

The following systemd-tmpfiles configuration ensures the directory exists and has the correct permissions.

[savona@putor tmpfiles.d]$ cat openssh.conf 
d /var/empty/sshd 711 root root -

The d type specifies that the file should be created and cleaned up. The next field is the path, then the mode and user/group specifications. The last - means that this configuration file does not specify an age, which means no automatic cleanup will occur.

Example 2: Remove a File If It Exists At Boot

The DNF Package Manager uses a systemd-tmpfiles configuration to remove files during boot.

[savona@putor tmpfiles.d]$ cat dnf.conf 
# Unlink the dnf lock files during boot
R /var/tmp/dnf*/locks/*
r /var/cache/dnf/download_lock.pid
r /var/cache/dnf/metadata_lock.pid
r /var/lib/dnf/rpmdb_lock.pid

The capital R type recursively removes the path and it's subdirectories. The lowercase r type is a simple remove of the file or empty directory. The next field is the path to me acted upon. There are no permissions or age requirements for these types.

Example 3: Create Then Clean Up Directories Based On Time

In this example the system creates and cleans up the basic /tmp and /var/tmp directories.

[savona@putor tmpfiles.d]$ cat tmp.conf 
q /tmp 1777 root root 10d
q /var/tmp 1777 root root 30d

The q tells the system to create a subvolume or directory. The 10d we see in the age field tells systemd-tmpfiles when it should be cleaned. In this case files that have ages past 10 days will be cleaned from the /tmp directory. Files aged past 30 days will be cleaned from the /var/tmp directory.

The date field, when set, is used to decide what files to delete when cleaning. If a file or directory is older than the current time minus the age field, it is deleted.

tmpfiles.d Man Page

The age field is more complex than I can cover here. For a more in-depth look at aging, see the tmpfiles.d man page linked in the resources section.

Example 4: Set MOTD to Display Hostname and Boot ID

Okay, so this may not be so "real world", but it's interesting. It also gives us a chance to look at the dynamic specifiers. These specifiers allow you to insert dynamic content into the path and argument fields. Let's take a loot at the example.

[savona@putor ~]$ cat /etc/tmpfiles.d/motd.conf 
r! /etc/motd
f! /etc/motd - - - - Hello and Welcome to %H The boot ID is %b

In the file above we are using ! to specify that these should be ran at boot. The first line starts with r! which says remove the path at boot. The second line starts with f! which creates the new MOTD file. The string in the arguments field specifies what the file content should be. The end result is the /etc/motd file being replaced with a new one. The file file contains the specifiers %H for hostname, and %b for boot id. Here is the new MOTD file in action:

savona@10.0.0.2's password: 
Hello and Welcome to putor The boot ID is 04cd5672cc27482680787ee4a028062b
Last login: Tue May 12 20:58:26 2020
[savona@putor ~]$ 

User Specific Examples

Example 1: Deleting Google Chrome Cache at Shutdown

This is a very user-centric example, although probably not a very practical one. In this example, we will setup a configuration file to empty the Google Chrome Cache directory using the user-space systemd-tmpfiles-setup.service.

First we create the configuration directory:

mkdir ~/.config/user-tmpfiles.d

Then we create a file inside that directory named delete-google-cach.conf with the following contents.

R	%h/.cache/google-chrome/Default/Cache/*

The R specifies to recursively remove files from the specified path. The systemd-tmpfiles-setup.service will empty the directory when it runs. We can test it by using the same command that is on the ExecStart line of the unit file.

[savona@putor ~]$ ls -lrt ~/.cache/google-chrome/Default/Cache/
total 27744
drwx------. 2 savona savona    4096 May 12 01:40 index-dir
-rw-------. 1 savona savona    5305 May 12 01:40 079a095ca14f7de9_0
-rw-------. 1 savona savona    8781 May 12 01:40 9a9531f2902f4d8b_0
-rw-------. 1 savona savona   71209 May 12 01:40 acb291bff88b882a_0

[savona@putor ~]$ systemd-tmpfiles --user --create --remove --boot

[savona@putor ~]$ ls -lrt ~/.cache/google-chrome/Default/Cache/
total 0 

Example 2: Create and Cleanup a Share Directory

I have a shared directory that my colleagues use to store large files. These files are usually drafts and no finish works are every put in this directory. It sometimes gets rather large. I created a systemd-tmpfiles confguration to ensure the creation of the directory, permissions are set, and a cleanup runs every month.

[savona@putor user-tmpfiles.d]$ cat clean-share.conf 
d /home/putorius 760 savona putorius 4w

Conclusion

In this article we discusses the service and path units that make up the systemd-tmpfiles suite. We also covered the configuration files and looked at some examples of it's use in real world.

This started out as a primer on systemd-tmpfiles, but I think I got a little long winded. If you have any comments or suggestion I would love to hear them in the comments below.

Resources & Further Learning