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
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
|Category||Requirements, Conventions or Software Version Used|
|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
$ – requires given linux commands to be executed as a regular non-privileged user
Scheduling a task via systemd involves the use of two different unit types:
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
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:
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:
|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.
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:
|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
* character matches any value. More examples can be found consulting the
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:
- The next time the timer will run (NEXT);
- How many times before the next time timer will run again (LEFT);
- The last time the timer ran (LAST);
- How much times has passed since the last time the timer ran (PASSED);
timer unitin which the schedule is set (UNIT);
service unitactivated 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
$ 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:
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
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
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.
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 (
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,
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
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
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