systemd/Timers
Systemd is capable of taking on a significant subset of the functionality of Cron through built in support for calendar time events (from systemd version 197) as well as monotonic time events. As of systemd version 212, it also has the foundations for being an anacron replacement through the use of the Persistent and OnCalendar options in timers.
Introduction
While Cron has been a stalwart on the Linux landscape for years, it still provides no way to detect job failures, establish job dependencies, or allocate processes to cgroups. If you require any of this functionality, systemd provides a good structure to set up job scheduling. While doing so is slightly more cumbersome than relying on dcron or cronie, the benefits are not insignificant:
- Last status and logging outputs can be got through journalctl. This enables proper debugging
- Systemd has a lot of options which are useful for setting the environment for the job to be done properly (eg IOSchedulingPriority, Nice or JobTimeoutSec)
- These jobs can be made to depend on other systemd units if required
Hourly, daily, weekly, monthly and yearly events
One strategy which can be used for recreating this functionality is through timers which call in targets. All services which need to be run hourly can be called in as dependencies of these targets. The strategy mentioned here has been detailed first in this blogpost.
First, the creation of a few directories is required:
# mkdir /etc/systemd/system/timer-{hourly,daily,weekly,monthly,yearly}.target.wants
The following files will need to be created in the paths specified in order for this to work.
Hourly events
/etc/systemd/system/timer-hourly.timer
[Unit] Description=Hourly Timer [Timer] OnBootSec=5min OnCalendar=hourly Unit=timer-hourly.target [Install] WantedBy=basic.target
/etc/systemd/system/timer-hourly.target
[Unit] Description=Hourly Timer Target StopWhenUnneeded=yes
Daily events
/etc/systemd/system/timer-daily.timer
[Unit] Description=Daily Timer [Timer] OnCalendar=daily Persistent=true Unit=timer-daily.target [Install] WantedBy=basic.target
/etc/systemd/system/timer-daily.target
[Unit] Description=Daily Timer Target StopWhenUnneeded=yes
As of systemd 212, the functionality of anacron is implemented in any timer which utilizes the OnCalendar option through the use of the Persistent option. If you would like to understand this functionality better, please see the systemd.timer(5) man page.
Weekly events
/etc/systemd/system/timer-weekly.timer
[Unit] Description=Weekly Timer [Timer] OnCalendar=weekly Persistent=true Unit=timer-weekly.target [Install] WantedBy=basic.target
/etc/systemd/system/timer-weekly.target
[Unit] Description=Weekly Timer Target StopWhenUnneeded=yes
Monthly events
/etc/systemd/system/timer-monthly.timer
[Unit] Description=Monthly Timer [Timer] OnCalendar=monthly Persistent=true Unit=timer-monthly.target [Install] WantedBy=basic.target
/etc/systemd/system/timer-monthly.target
[Unit] Description=Monthly Timer Target StopWhenUnneeded=yes
Yearly events
/etc/systemd/system/timer-yearly.timer
[Unit] Description=Yearly Timer [Timer] OnCalendar=yearly Persistent=true Unit=timer-yearly.target [Install] WantedBy=basic.target
/etc/systemd/system/timer-yearly.target
[Unit] Description=Yearly Timer Target StopWhenUnneeded=yes
Adding events
Adding events to these targets is as easy as dropping them into the correct wants folder. So if you wish for a particular job to take place daily, create a systemd service file.
For example, if you wish to run foo.service daily (which runs program bar), you would create the following file:
/etc/systemd/system/foo.service
[Unit] Description=Starts program bar Wants=timer-daily.timer [Service] User= # Add a user if you wish the service to be executes as a particular user, else delete this line Type= # Simple by default, change it if you know what you are doing, else delete this line Nice=19 IOSchedulingClass=2 IOSchedulingPriority=7 ExecStart=/usr/bin/bar --option1 --option2 # More than one ExecStart can be used if required [Install] WantedBy=timer-daily.target
Then, start and enable the service.
Enable and start the timers
# systemctl enable timer-{hourly,daily,weekly,monthly,yearly}.timer
# systemctl start timer-{hourly,daily,weekly,monthly,yearly}.timer
Starting events according to the calendar
If you wish to start a service according to a calendar event and not a monotonic interval (i.e. you wish to replace the functionality of crontab), you will need to create a new timer and link your service file to that. An example would be:
/etc/systemd/system/foo.timer
[Unit] Description=foo timer [Timer] # To add a time of your choosing here, please refer to systemd.time manual page for the correct format OnCalendar=Mon-Thu *-9-28 *:30:00 Persistent=true Unit=foo.service [Install] WantedBy=basic.target
The service file may be created the same way as the events for monotonic clocks. However, take care to put them in the /etc/systemd/system/ folder.
Custom/example service files
Reflector
This service file may be used to update pacman's mirrorlist daily using the reflector script.
/etc/systemd/system/timer-daily.target.wants/reflector.service
[Unit] Description=Update the mirrorlist [Service] Nice=19 IOSchedulingClass=2 IOSchedulingPriority=7 Type=oneshot ExecStart=/usr/bin/reflector --protocol http --latest 30 --number 20 --sort rate --save /etc/pacman.d/mirrorlist
The modprobed_db service
This service is of great use to people who compile their own kernels because it reduces compilation time by a significant amount. Refer to the Modprobed_db page for further details.
/etc/systemd/system/modprobed_db.service
[Unit] Description=Run modprobed_db Wants=timer-daily.target [Service] Type=oneshot RemainAfterExit=yes User=enter user here Nice=19 IOSchedulingClass=2 IOSchedulingPriority=7 ExecStart=/usr/bin/modprobed_db store [Install] WantedBy=timer-daily.target
Then, start and enable the service
Hosts-update service
Package hosts-updateAUR uses mvps blocklist to update /etc/hosts.
/etc/systemd/system/timer-daily.target.wants/hosts-update.service
[Unit] Description=Update hosts file After=network.target [Service] Nice=19 IOSchedulingClass=2 IOSchedulingPriority=7 ExecStart=/usr/bin/hosts-update [Install] WantedBy=timer-daily.target
See also
- https://fedoraproject.org/wiki/Features/SystemdCalendarTimers - systemd calendar timers on the Fedora Project wiki