Sub-Minute Job Execution with Systemd Timers

June 15, 2016
Automation

Sometimes there’s a small job that needs to be run every X seconds. But having a daemon running calls sleep(3) is either overkill or you’re too lazy to implement starting/stopping/signal handling.

Cron only has a one-minute accuracy, so that won’t help in this case. Fortunately, systemd has the possiblity to run services at configured times or with a monotonic interval using timer units.

[Unit]
Description=A job that stores the time

[Service]
Type=oneshot
ExecStart=/usr/local/bin/foo.sh

/etc/systemd/system/foo.service

[Unit]
Description=Runs every 15 secs

[Timer]
OnActiveSec=0 # Run when activated
OnUnitActiveSec=15 # Run every 15 seconds
AccuracySec=500msec # Don't delay too much

[Install]
WantedBy = multiuser.target

/etc/systemd/system/foo.timer

#!/bin/sh
date >> /tmp/timestamp

/usr/local/bin/foo.sh

Enable and start the timer: systemctl enable --now foo.timer and lo and behold:

Wed Jun 15 16:26:44 CEST 2016
Wed Jun 15 16:27:00 CEST 2016
Wed Jun 15 16:27:15 CEST 2016
Wed Jun 15 16:27:31 CEST 2016
Wed Jun 15 16:27:46 CEST 2016
Wed Jun 15 16:28:02 CEST 2016

/tmp/timestamp

Note that the timestamps are not exactly 15 seconds apart. This is because AccuracySec is set to half a second. Systemd attempts to limit the number of wake ups and to schedule timers simultaniously. For a more precise interval, AccuracySec can be set to 1us.

An advantage of this approach (compared to to cron) is the fact that the stdout/stderr output of foo.service goes directly to the journal. This allows foo.sh (or whatever script used) to be extremely simple.

The Arch Linux wiki on systemd timers has a good section on using systemd timers as a replacement for cron, including sending emails on job failure.