Skip to main content

Command Palette

Search for a command to run...

Systemd Timers - a better alternative for crontab?

Updated
7 min read
Systemd Timers - a better alternative for crontab?

Cron is the ubiquitous job scheduler in the Linux world. Anyone who wants to run a periodic automation or a one-off script at a predetermined time interval has used cron. Plenty of housekeeping tasks run as scheduled jobs by default on all Linux systems. The files under /etc/cron.* will give you an idea on these cron jobs.

For a very long time crontab has been the undisputed choice for scheduling jobs on Linux systems. But with the wide adoption of systemd as the system and service manager, it has given us a new option - systemd timers.

Most casual Linux users consider systemd as a modern replacement for the SysV init system. While it is partially true, systemd is much more than that. To quote the systemd website:

systemd provides aggressive parallelization capabilities, uses socket and D-Bus activation for starting services, offers on-demand starting of daemons, keeps track of processes using Linux control groups, maintains mount and automount points, and implements an elaborate transactional dependency-based service control logic.

While a discussion on the systemd capabilities is outside the scope of this article, the following points will serve as a refresher to better understand this article:

  • Systemd owns PID1 and is started by the Linux kernel

  • All other processes in the system are descendants of systemd

  • It is responsible for filesystem initialization

  • The fundamental building block of the systemd ecosystem is the concept of units

  • A unit is a system resource that systemd knows how to manage

  • There are different types of units, including services, devices, mountpoints, sockets, etc

  • The definition and configuration of a unit is stored in unit files under different directories managed by systemd

  • There are system level units and user level units

With that background set, let's get back to running crons with systemd timers.

Systemd Timers

Systemd provides an alternative to crons in the form of systemd timers. Timers are a type of unit files that define how a job or a service can be run on calendar events or monotonic events. Calendar events are similar to cron time and date fields, set based on a calendar time, and can be recurring as well. Monotonic events are configured as a time delta from the occurrence of an event, say boot time.

  • A systemd timer unit file will have the extension .timer

  • For every systemd timer unit file, there will be another unit file with the same name and a .service extension

  • The timer unit defines the “when to run”, whereas the service unit defines the “what to run”

Let's use an example to demonstrate how timers work.

Consider the scenario of taking a MySQL backup every three hours. A crontab entry would look like this:

0 */3 * * *  /usr/local/bin/mysql-backup.sh

And the script would be

cat /usr/local/bin/mysql-backup.sh

#!/bin/bash
/usr/bin/mysqldump inventory  > /opt/db-backup/inventory-db-backup_`date +%H-%d-%m-%Y`.sql

The authentication will be taken care of with /root/.my.cnf

[client]
host=localhost
user=MYSQL_BACKUP_USER
password=MYSQL_BACKUP_PASSWORD

Now let us see how to move this cron to a systemd timer.

First we need to translate our backup script into a systemd service.

cat /etc/systemd/system/mysql-backup.service

[Unit]
Description="MySQL Backup Service"
[Service]
ExecStart=/usr/local/bin/mysql-backup.sh

Now we need to create the timer unit


cat /etc/systemd/system/mysql-backup.timer
[Unit]
Description="Run mysql-backup.service every 3 hours"

[Timer]
OnCalendar=*-*-* 00/3:00:00
Unit=mysql-backup.service

[Install]
WantedBy=multi-user.target

Most parts of these unit files are self-explanatory and are not different from normal unit files. The timer section describes what service to run ( mysql-backup.service ) and one or more time options. In this particular case, we are giving a specific calendar timing that executes the service once every three hours. We will look at a few more timer options later.

Now, verify the files are syntactically correct.

sudo systemd-analyze verify /etc/systemd/system/mysql-backup.*

If this command doesn't return any output, then we are good to proceed.

Reload systemd to update the system about new unit files.

sudo systemctl daemon-reload

Enable and start the timer unit.

sudo systemctl enable --now mysql-backup.timer

The timer is active now.

sudo systemctl status mysql-backup.timer
● mysql-backup.timer - "Run mysql-backup.service every 3 hours
     Loaded: loaded (/etc/systemd/system/mysql-backup.timer; disabled; vendor preset: enabled)
     Active: active (waiting) since Sat 2023-03-08 21:07:23 IST; 3s ago
    Trigger: Sun 2023-03-09 00:00:00 IST; 2h 52min left
   Triggers: ● mysql-backup.service

As you can see, the timer is enabled and the next run time is also listed.

Now let us look at the few ways the timers can be configured. As mentioned in the beginning, there are two categories of timers - real time ( calendar based ) and monotonic ( based on events ).

Monotonic timers

Monotonic timers are triggered after a specific time elapsed from an event, like boot time. There are different options to configure the monotonic timers, some of them are given below.

  • OnBootSec: time after the machine boots up

  • OnActiveSec: time after the timer unit is activated

  • OnUnitActiveSec: time after the service unit was last activated

  • OnUnitInactiveSec: time after the service unit was last deactivated

  • OnStartupSec: time after the service manager is started

There are various formats in which you can provide values to the monotonic timer options. Some of them are

  • 5hours

  • 34minutes

  • 5hours 34minutes

  • 1y 3month

Real-time timer

Triggered by calendar events, the real-time timers have only one option: “OnCalendar”. This is the option that closely resembles crontab timers, so let's have a quick comparison.

Crontab Format:   minute hours day-of-the-month month day-of-the-week

This is a five-part format that can use *, absolute values, ranges, and lists for each part.

Systemd Timer Format:  Day Of the week      Year-Month-Date      Hour:Minute:Second

This three-part format works as follows:

  • First part is the day of the week with 3 character values from Mon to Sun

  • Second part is Year-Month-Date in number format with 4 digit years, two digit month and day

  • Third part is time - 2 digits per hour, minute, and second

  • A continuous range of values can be indicated with two dots. Eg: Mon..Wed mean Mon, Tue, Wed

  • A list of values can be indicated with a comma. Eg: Sat,Sun

  • Asterisk can be used as a wild card to match all valid values. Eg: “*” in the first part means all weekdays - Mon..Sun

  • “/” can be used as a repetition option

  • Default values can be skipped and the syntax can be shortened in specific ways

  • Shorthands like minutely, hourly, yearly, etc can be used as a timer

The systemd time manpage covers the time formats in detail. Refer - https://man.archlinux.org/man/systemd.time.7

A few values for OnCalendar option are:

  • Sat,Sun --* 23:00 - Every Saturday and Sunday at 11

  • 2023-02-14 00:00:01 - 1 second into 14 Feb 2023

  • --* 00/3:00:00 - Every three hours

  • 23:40/5 - Every day every 5 minutes starting 11:40 PM

The variety of values in the OnCalendar option can be confusing. To ensure we provide the right values, systemd provides a command-line option. Let us validate the last value from this list as follows:

sudo systemd-analyze calendar --iterations=2 "23:40/5"
  Original form: 23:40/5
Normalized form: *-*-* 23:40/5:00
    Next elapse: Sat 2023-03-08 23:40:00 IST
       (in UTC): Sat 2023-03-08 18:10:00 UTC
       From now: 17min left
       Iter. #2: Sat 2023-03-08 23:45:00 IST
       (in UTC): Sat 2023-03-08 18:15:00 UTC
       From now: 22min left

As you can see, the command interprets the shorthand and expands into the normalized form, it also provides when the next few occurrences of the schedule will be ( controlled with –iterations ). It also tells you how much time from your current time will the event occur.

Conclusion

As you can see, the systemd timers are very versatile. Crontab provides the simple and efficient form of scheduling, and is available ubiquitously on all Linux operating systems, unlike systemd, which is not guaranteed to be available on all variants of Linux. Having said that, systemd is available on most popular operating systems and these are some of the advantages of timers.

  • With service units, the jobs can be independently tested and run anytime without timers

  • A job can be configured to depend on another systemd unit. For example, run the MySQL backup unit only if the mysqld service is running.

  • Can be resource controlled with cgroups and slices

  • Easy debugging with journalctl

  • Time formats allow more control. Years and seconds are supported.

Do experiment with systemd timers. Most of the systemd options that can be used to customize the unit files can be used to better configure your timers and services. If you are running scheduled jobs for production systems, using the systemd timers makes more sense, and is easier to automate with configuration management tools.