How to schedule tasks with systemd timers in Linux

The traditional way to schedule tasks on Linux, is to use the cron daemon, specifying time intervals and
commands to be executed in crontabs.

Systemd, the relatively new init system now adopted by all the major Linux distributions, among the other things, provides the ability to schedule tasks using dedicated units, called timers. In this article we will learn how they are structured and some examples of their usage.

In this tutorial you will learn:

  • The basic structure of systemd timers;
  • How to create monotonic and realtime timers;
  • How to list and inspect active timers;
  • How to enable timers;
  • How to use transient timers;

Software Requirements and Conventions Used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution-independent
Software Systemd
Other Knowledge of basic Systemd concepts
Conventions # – requires given linux commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux commands to be executed as a regular non-privileged user

Basic usage



Scheduling a task via systemd involves the use of two different unit types: timers and services. The former are unit files with the .timer extension: in them, we define the job schedule and set the service unit that should be triggered. The latter are the most common unit types: they are used to define services on modern Linux distributions and are identified by the .service extension.

We use service units to set the actual command to be executed (if you are not familiar with the basic systemd concepts, you may want to take a look at our article about systemd services).

Depending on how the schedule is created, a timer can be:

  1. Monotonic
  2. Realtime

Monotonic timers

Systemd provides a list of keywords we can use in a timer unit to schedule the execution of a task a certain amount of time after a predefined event takes place. The keywords must be used in the [Timer] section of the timer unit.

Let’s see them and explain their meaning:



Keyword Meaning
OnActiveSec Schedule the task relatively to the time when the timer unit itself is activated
OnBootSec Schedule task relatively to the system boot time
OnStartupSec Schedule the task relatively to the time when Systemd started
OnUnitActiveSec Schedule the task relatively to the last time the service unit was active
OnUnitInactiveSec Schedule the task relatively to the last time the service unit was inactive

As can be easily guessed from the name of the keys, “seconds” are used as the default unit of time. We can, however, specify a different unit after the value (e.g. 15m – fifteen minutes). As we will see later, the keywords can be combined inside a timer unit.

Realtime timers

An event can also be scheduled in “absolute” terms, similarly to how we would define it via cron, using another the OnCalendar keyword and allowed time encodings.

Here are some examples:



Time specification Explanation
Wed 18:00:00 The task will be executed each Wednesday at 18:00
Mon..Wed *-5-27 The task will be executed the 27th of May of each year, but only on days from Monday to Wednesday
2020-05-27 The task will be executed on the 27th of May of the year 2020 at 00:00:00
Thu,Fri 2020-*-1,5 11:12:13 The task will be executed at 11:12:13 of the first and the fifth day of each month of the year 2020, but only if the day is a Thursday or a Friday
*:0/2 The task will be executed every two minutes starting from the minute 0
15/2 The task will be executed every two hours starting from 3:00 pm
hourly The task will be executed at the beginning of each hour
daily The task will be executed each day at 00:00:00
weekly The task will be executed every Monday at 00:00:00
monthly The task will be executed the first day of each month at 00:00:00

The weekdays, if specified, must be in English, either in the abbreviated (Wed) or complete form (Wednesday) (the case doesn’t matter).

We can provide a list of time values using the , character and specify a range of values using ... A * character matches any value. More examples can be found consulting the systemd.time manpage.

Listing active timers

To list all the active timer units in our system, we can launch the list-timers subcommand of systemctl. Unless the --all option is passed to the command, only the active timers are included in the result. Here is an example of the output produced by the command:

$ systemctl list-timers
NEXT                         LEFT          LAST                         PASSED       UNIT                         ACTIVATES
Sun 2020-01-19 19:36:06 CET  5h 15min left Sat 2020-01-18 10:38:59 CET  1 day 3h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2020-01-20 00:00:00 CET  9h left       Sun 2020-01-19 00:00:16 CET  14h ago      man-db.timer                 man-db.service
Mon 2020-01-20 00:00:00 CET  9h left       Sun 2020-01-19 00:00:16 CET  14h ago      shadow.timer                 shadow.service

The report is very detailed. It includes 6 columns, which describes, in order:

  1. The next time the timer will run (NEXT);
  2. How many times before the next time timer will run again (LEFT);
  3. The last time the timer ran (LAST);
  4. How much times has passed since the last time the timer ran (PASSED);
  5. The timer unit in which the schedule is set (UNIT);
  6. The service unit activated by the timer (ACTIVATES).


A real world example

Let’s examine the man-db.timer timer. To inspect the unit, we can use systemctl and the cat subcommand:

$ systemctl cat man-db.timer

Here is the timer definition:

[Unit]
Description=Daily man-db regeneration
Documentation=man:mandb(8)

[Timer]
OnCalendar=daily
AccuracySec=12h
Persistent=true

[Install]
WantedBy=timers.target

The first thing we can notice is the [Unit] stanza, which is common to all the systemd unit types. Here it is used to provide a description of the unit: we can see that the timer is used to perform a “daily regeneration of man-db”.

The section which interests us the most, however, is [Timer]. This stanza is specific to timer units: it is where the schedule is defined. The OnCalendar keyword is used to set a daily realtime schedule.

We can also observe two other keywords are used: AccuracySec and Persistent. The former is used to establish a maximum delay in which the service can be launched. In this case the value is 12h, so the command could be delayed for a maximum of 12 hours. The default value for AccuracySec is 1 minute; the best accuracy is obtained with the 1ns notation (1 nanosecond).



The other keyword, Persistent, takes a boolean value: if set to true, the last time the service was triggered by the timer is saved to disk. If for whatever reason a scheduled run is missed, the next time the timer unit is activated, the service is launched immediately, if in the elapsed time it would have been triggered at least once. This can be useful, for example, to execute schedules missed due to a system powered down, the next time the machine is powered on.

By taking a closer look at the timer definition, we can notice that the service to be triggered is not explicitly mentioned: when this happens, Systemd looks for a service unit with the same name of the timer (so in this case man-db.service). To reference a service unit explicitly, we must use the Unit keyword.

Activating a timer

Activating a timer is quite simple. All we have to do is to place it, together with the service is should trigger, inside the /etc/systemd/system directory. With all files in place, we run:

$ sudo systemctl start <timer-unit-name>.timer

To make a timer be automatically activated at boot (or when another specific target is reached), all we have to do is to make sure it has an [Install] stanza, where we specify when the activation should happen.

In the example above the WantedBy keyword is used to establish a reverse (weak) dependency of a specific target unit (timers.target – a target reached quite early in the boot process) on the timer unit we are configuring: before that target is reached, our unit should be activated.

Transient timers

It is possible to schedule the execution of tasks “on the fly”, without manually creating dedicated timer and service units by using systemd-run. The command creates temporary units (they will not survive a reboot) inside the /run/systemd/transient directory if run globally, and inside /run/user/<user-uid>/systemd/transient directory if launched as a specific user (--user option).

Let’s see an example. Suppose we want date and time to be logged to a file every minute. We would run:

$ systemd-run --user --on-calendar '*:0/1' /bin/sh -c "date >> ~/log.txt"
Running timer as unit: run-r81a4fef38154401bbd8cdbd1e5c19d04.timer
Will run service as unit: run-r81a4fef38154401bbd8cdbd1e5c19d04.service

As we can see from the output of the command, two temporary units have been created, run-r81a4fef38154401bbd8cdbd1e5c19d04.timer and run-r81a4fef38154401bbd8cdbd1e5c19d04.service.

If we examine the log file, we can see that the timer is working correctly:

$ cat ~/log.txt
Mon 20 Jan 2020 11:20:54 AM CET
Mon 20 Jan 2020 11:21:54 AM CET
Mon 20 Jan 2020 11:22:54 AM CET
Mon 20 Jan 2020 11:23:54 AM CET
Mon 20 Jan 2020 11:24:54 AM CET
Mon 20 Jan 2020 11:25:54 AM CET
Mon 20 Jan 2020 11:26:54 AM CET

To remove/disable a transient timer, all we have to do is to stop it. In this case we would run:

$ systemctl --user stop run-r81a4fef38154401bbd8cdbd1e5c19d04.timer

Conclusions

In this tutorial we learned how we can schedule system tasks using systemd timers as an alternative to cronjobs. We saw the basic structures behind timers, how we can define monotonic and realtime schedules via dedicated keywords, such as OnBootSec or OnCalendar, how to list and examine active timers, how to enable and disable them.

Finally, we saw how to use transient timers. In this article you should find everything you need to get started with timers. For more detailed information you may want, however, to take a look at the official documentation, either online or by consulting the systemd.timer manpage.



Comments and Discussions
Linux Forum