Systemd timers
Introduction
Systemd timers are the equivalent of cron and cronjobs. For modern Linux systems they most likely are a full replacement, including additional features.
Timers in systemd can be recognized by their extension .timer and are plaintext files that include their configuration. They describe what should happen, when, and if there are specific conditions that needs to be met. It may include additional information like documentation and comments.
One important thing to know upfront when using timers is that a timer does not come alone. It is a trigger for a service unit (.service) and therefore should have the same name. The only difference in the name is obviously the file extension. If you are new to systemd units, have a look at systemd units and their purpose.
Additional benefits of timers
Timers have a few additional benefits that typically does not exist in cron or requires additional work.
Detailed management
Systemd units and their status are tracked carefully. Details such as activation and last execution are stored. This makes it easy to see when a task was performed. In the case of timers, we can also see when it will be performed in the near future. With cron this is harder to see, especially when jobs are divided over multiple locations (crontab, cron.hourly, cron.daily, cron.weekly, etc).
Delayed start after boot
One of the options is to start a task just after the system is booted, like updating software packages. But as the system will be very busy just after the start, it might be useful to wait for a few minutes. This can be achieved using the OnBootSec setting, part of the Timer section.
- Setting: OnBootSec
- Value: time
- Section: Timer
- Example: OnBootSec=5min
Delayed start with random time
When having many systems performing a task at the the same hour and minute, it can cause unneeded strain on the network, internet connection, or receiving systems. Timers can solve this issue by delaying the start time of a task with a random interval. The setting is RandomizedDelaySec and part of the Timer section.
While the setting would indicate the time is to be set in seconds, a time can also be specified, like 12h. This way the task will be delayed between 0 seconds and half a day.
- Setting: RandomizedDelaySec
- Value: time
- Section: Timer
- Example: RandomizedDelaySec=12h
Delay after unit activation
Sometimes you want to create a timer, but not directly active it. Maybe there is not enough data to process, or you want to wait till the next day. By defining OnUnitActiveSec in the Timer section, we can delay execution as well.
- Setting: OnUnitActiveSec
- Value: time
- Section: Timer
- Example: OnUnitActiveSec=24h
See available systemd timers
On a regular Linux distribution there will be timers defined by the distribution itself and as part of common software packages. Use the subcommand list-timers to show the existing timers.
# systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Fri 2024-05-24 10:10:35 UTC 2h 27min left Thu 2024-05-23 04:56:31 UTC 1 day 2h ago man-db.timer man-db.service
Fri 2024-05-24 11:48:14 UTC 4h 5min left Thu 2024-05-23 19:57:03 UTC 11h ago fwupd-refresh.timer fwupd-refresh.service
Fri 2024-05-24 16:01:53 UTC 8h left Thu 2024-05-23 19:56:56 UTC 11h ago apt-daily.timer apt-daily.service
Fri 2024-05-24 20:10:39 UTC 12h left Fri 2024-05-24 04:47:52 UTC 2h 55min ago motd-news.timer motd-news.service
Sat 2024-05-25 00:00:00 UTC 16h left Fri 2024-05-24 00:00:21 UTC 7h ago dpkg-db-backup.timer dpkg-db-backup.service
Sat 2024-05-25 00:00:00 UTC 16h left Fri 2024-05-24 00:00:21 UTC 7h ago logrotate.timer logrotate.service
Sat 2024-05-25 00:05:14 UTC 16h left Fri 2024-05-24 00:05:14 UTC 7h ago update-notifier-download.timer update-notifier-download.service
Sat 2024-05-25 00:16:14 UTC 16h left Fri 2024-05-24 00:16:14 UTC 7h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Sat 2024-05-25 06:06:59 UTC 22h left Fri 2024-05-24 06:50:34 UTC 52min ago apt-daily-upgrade.timer apt-daily-upgrade.service
Sun 2024-05-26 03:10:58 UTC 1 day 19h left Sun 2024-05-19 03:11:03 UTC 5 days ago e2scrub_all.timer e2scrub_all.service
Mon 2024-05-27 01:04:09 UTC 2 days left Mon 2024-05-20 00:01:32 UTC 4 days ago fstrim.timer fstrim.service
Mon 2024-05-27 07:15:58 UTC 2 days left Thu 2024-05-23 19:56:56 UTC 11h ago update-notifier-motd.timer update-notifier-motd.service
12 timers listed.
Pass --all to see loaded but inactive timers, too.
Besides the timing (next, left, last, passed), we can see the unit name, but also the column ACTIVATES.
How to see the configuration of a timer
In the column UNIT, when using list-timers, the file name is shown. This file name is not the full path, so it can be stored in a few common locations. Fortunately, you don’t have to know where it is located (yet). With the subcommand cat we can easily see the content of the timer file and its location.
# systemctl cat man-db.timer
# /lib/systemd/system/man-db.timer
[Unit]
Description=Daily man-db regeneration
Documentation=man:mandb(8)
[Timer]
OnCalendar=daily
RandomizedDelaySec=12h
Persistent=true
[Install]
WantedBy=timers.target
When using systemctl cat
, the first line is displayed as a comment and refers to the full path of the unit file.
Status of a timer
To learn more about a particular timer and its status, we can query it using the status subcommand. Similar to a service, this will give us more insights, but tailored to the timer itself.
# systemctl status fstrim.timer
● fstrim.timer - Discard unused blocks once a week
Loaded: loaded (/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
Active: active (waiting) since Wed 2024-05-08 23:56:25 UTC; 2 weeks 1 day ago
Trigger: Mon 2024-05-27 01:04:09 UTC; 2 days left
Triggers: ● fstrim.service
Docs: man:fstrim
May 08 23:56:25 webdev01 systemd[1]: Started Discard unused blocks once a week.
In this example, we can see that is is active and in 2 days the timer will be triggered. The Triggers line shows us the relevant service (fstrim.service).
Timer types and schedule
Timers are scheduled by their type. A monotonic timer is activated based on another event, such as activation of the unit or boot time. Examples of this type were already covered in the benefits above.
Another type of a real-time timer. It is planned by using a calendar event, like the day name (Monday) or specific hour of the day. This type is commonly used to run tasks on a repeating rate.
By day
When using the OnCalendar option, we can schedule a task daily, exactly at the hour/minute/second mark.
OnCalendar=Mon..Sun *-*-* 4:00:00
Another option is defining multiple ranges.
OnCalendar=Mon..Fri *-*-* 22:00
OnCalendar=Sat,Sun *-*-* 14:00
Multiple hours is also possible. To run a task daily at 6 AM and 6 PM, define the entry without a day and only the hours, separated by a comma.
OnCalendar=*-*-* 6,18:00
To repeat a task multiple times an hour, we can define this using a slash. For example, having the systemd timer run every 5 minutes.
OnCalendar=*-*-* *:0/5:00
Weekly
The easiest way to plan a task weekly is by using the weekly value.
OnCalendar=weekly
An alternative is to define the specific day and hour.
OnCalendar=Sun *-*-* 06:00:00
By specific day of the month
It is also possible to plan a task at the first of the month, or a specific month of the year.
OnCalendar=*-*-1 00:01
In March, daily? Set the month
OnCalendar=*-3-* 00:01
Only on Sunday if it is an even month.
OnCalendar=Sun *-2,4,6,8,10,12-* 00:01
As you can see, this system of scheduling is very flexible.
Testing your timer
Due to its flexibility, one might make an error. To learn when the next planned action would happen, we can use the systemd-analyze command with its subcommand calendar.
# systemd-analyze calendar "Sun *-6,8-* 00:01"
Original form: Sun *-6,8-* 00:01
Normalized form: Sun *-06,08-* 00:01:00
Next elapse: Sun 2024-06-02 00:01:00 UTC
From now: 1 week 1 day left
Systemd timers commands
Command | Goal |
---|---|
systemctl cat UNIT.timer | Show the content of a timer |
systemctl enable UNIT.time | Enable the timer |
systemctl list-timers | Show active timers |
systemctl list-timers PATTERN | Show active timers that match a pattern (e.g. log*) |
systemctl list-timers --all | Show all timers, including inactive timers |
systemctl start UNIT.timer | Start the timer |
systemctl status UNIT.timer | Show status of the timer |
systemctl stop UNIT.timer | Stop the timer |
Converting from cron to systemd timers
Need to convert entries from cron to systemd? Here are a few common ones.
Cron | systemd timer |
---|---|
hourly | OnCalendar=--* *:00:00 |
daily | OnCalendar=--* 00:00:00 |
weekly | OnCalendar=Sun --* 00:00:00 |
monthly | OnCalendar=--01 00:00:00 |