Replacing cron jobs with systemd timers
Cron is fine. I used it for a decade. But every time a job silently failed, I had to go looking for why, and the answer was usually "it never ran" or "it ran with a different PATH". Timers fix both, at the cost of writing two small files instead of one line.
The two files
A service describes what to run. A timer describes when. Here is my nightly backup:
# /etc/systemd/system/backup-docs.service
[Unit]
Description=Back up documents to the external disk
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-docs.sh
Nice=10
IOSchedulingClass=idle
# /etc/systemd/system/backup-docs.timer
[Unit]
Description=Nightly documents backup
[Timer]
OnCalendar=*-*-* 03:15
RandomizedDelaySec=20min
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup-docs.timer
The three properties I always set
| Property | Why |
|---|---|
Persistent=true | If the machine was off at 03:15, the job runs at the next boot instead of waiting a day. |
RandomizedDelaySec | Stops several jobs from hitting the disk at exactly the same second. |
IOSchedulingClass=idle | The backup yields to anything interactive, so nobody notices it. |
Finding out what happened
This is the real reason I switched. Every run is logged with its exit code:
systemctl list-timers backup-docs.timer
systemctl status backup-docs.service
journalctl -u backup-docs.service --since yesterday
No more redirecting output to a file in /tmp and hoping.
Testing the schedule
OnCalendar syntax takes a moment to get used to. systemd-analyze tells you exactly when an expression fires next:
systemd-analyze calendar "Mon..Fri *-*-* 07:30"
systemd-analyze calendar --iterations=3 weekly
When I still use cron
On machines I do not own, or for a quick one-off. For anything I want to rely on, it is a timer.