Timer example in systemd
We can use systemd
instead of cron timers in Unix. Using systemd
timers is easy and provides numerous benefits over using Cron which are documented below. These timers more are complicated than cron, however, they are much more powerful.
Creating a systemd
timer
Let us look at the logical hierarchy of a timer on systemd and the files associated before we look at an example.
Logical Requirements
To create a systemd
timer the following are required:
- A service of type
oneshot
that executes the program - A timer that will run the oneshot service on an interval
- A program that needs to run at specified times
Files Required
We will create three files for these requirements:
- Install
example.service
in the directory/lib/systemd/system/
- Install
example.timer
in the directory/lib/systemd/system/
- Install
example
script in the directory/usr/local/bin/
Quickstart
Here are the commands to install, run, and verify the timer is working:
$ sudo bash create-example-timer # the script below installs required systemd
$ sudo systemctl start example.timer # let systemd run the timer
$ sudo systemctl status example.timer # see when the timer will activate
$ sudo systemctl status example # see the status of the service and exit code of the script
$ ls -l /tmp/example # we see file timestamp update every 3 minutes
Example
Let’s create an example that will touch
a file every 3 minutes. The touch
command will merely change the file’s timestamp and can be see by long listing the file - ls -l
.
Below is a script called create-example-timer
that we can run with sudo bash create-example-timer
to get our example running.
#!/bin/bash
SYSTEMDPATH=/lib/systemd/system
PROGRAM=/usr/local/bin/example
EVENTS="*-*-* *:00/3:00" # every 3 minutes
# create the service file that runs our program
cat << EOF > $SYSTEMDPATH/example.service
[Unit]
Description=An example oneshot service that runs a program
[Service]
Type=oneshot
ExecStart=$PROGRAM
[Install]
WantedBy=multi-user.target
EOF
# create the timer that kicks off our service every 3 minutes
cat << EOF > $SYSTEMDPATH/example.timer
[Unit]
Description=A timer that runs our example service
[Timer]
OnCalendar=*-*-* *:00/3:00
[Install]
WantedBy=timers.target
EOF
# create a program that touches a file
cat << EOF > $PROGRAM
#!/bin/sh
touch /tmp/example
EOF
chmod 755 $PROGRAM
Running the Example
Once we’ve dropped the files down on the filesystem we can view the service and timer with systemd
.
Starting the Timer
We can see the timer exists, however, but it isn’t active yet.
$ systemctl status example.timer
● example.timer - A timer that runs our example service
Loaded: loaded (/lib/systemd/system/example.timer; disabled; vendor preset: enabled)
Active: inactive (dead)
Trigger: n/a
Let’s start the timer and see the state change from inactive to active.
$ sudo systemctl start example.timer
$ sudo systemctl status example.timer
● example.timer - A timer that runs our example service
Loaded: loaded (/lib/systemd/system/example.timer; disabled; vendor preset: enabled)
Active: active (waiting) since Sat 2022-07-09 12:09:58 MDT; 4s ago
Trigger: Sat 2022-07-09 12:12:00 MDT; 1min 57s left
Jul 09 12:09:58 penguin systemd[1]: Started A timer that runs our example service.
We can also list all active timers. This command allows us to see when the next activation time is as well as the last time it was activated successfully. It is useful if there are many timers to view in the system.
$ systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sat 2022-07-09 11:36:00 MDT 18s left Sat 2022-07-09 11:33:22 MDT 2min 18s ago example.timer example.service
...
6 timers listed.
Pass --all to see loaded but inactive timers, too.
Listing the Service
We can see the service and even start it. Since this service is oneshot
it will show inactive as it runs and exits. We are observing the last run of the service that was activated by the timer and it’s exit code.
$ systemctl status example
● example.service - An example oneshot service that runs a program
Loaded: loaded (/lib/systemd/system/example.service; disabled; vendor preset: enabled)
Active: inactive (dead) since Sat 2022-07-09 11:36:22 MDT; 20s ago
Process: 3367 ExecStart=/usr/local/bin/example (code=exited, status=0/SUCCESS)
Main PID: 3367 (code=exited, status=0/SUCCESS)
Confirming the timer is working
In general the systemctl status example.timer
provides us information on when the service was run last and will run in the future. The systemctl status example
will show us the exit code of the program that we activated.
To see our program actually ran we can either do an ls -l /tmp/example
or watch ls -l /tmp/example
and see the timestamp of the file is changing every 3 minutes.
Events
The heart of this example is the timer’s OnCalendar
field which runs every 3 minutes. This is done with the value *-*-* *:00/3:00
. To understand this format see the manual page for the time format. This page provides the syntax and examples. Once the OnCalendar
value is constructed we can easily test it with systemd-analyze. This command will print some details about the when the timer will kick off.
$ systemd-analyze calendar '*-*-* *:00/3:00'
Normalized form: *-*-* *:00/3:00
Next elapse: Sat 2022-07-09 11:30:00 MDT
(in UTC): Sat 2022-07-09 17:30:00 UTC
From now: 30s left
The systemd-analyze
command will ensure our OnCalendar
value is correct.
$ systemd-analyze calendar '*-* zz*:00/3:00'
Failed to parse calendar specification '*-* zz*:00/3:00': Invalid argument
Advantages to systemd timers over cron
As we’ve seen a systemd
timer has more moving parts than a simple entry in the crontab
file. However, by using systemd
we have some advantages:
- Timers can be examined and viewed by all users on the system and are not tied to a specific user
- Timers can have complex dependencies based on the
systemd
dependency tree. Thus, we can trigger a timer based on services, boot-up, startups, how long services have been active or inactive. - Through
systemctl
andjournalctl
we can more easily debug our timers - The events that the timer can trigger on are more advanced than just every X seconds. Timers can delay randomly, use realtime clocks, trigger off timezone changes, and even wake the system on trigger.
This information has been taken from the systemd.timer manpage.
References
time format manpage systemd.timer manpage systemd-analyze manpage