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
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:
- Monotonic
- 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:
- 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);
- The
timer unit
in which the schedule is set (UNIT); - 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.