Radon To Go · notes

Replacing cron jobs with systemd timers

January 22, 2026 · 4 min read

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

PropertyWhy
Persistent=trueIf the machine was off at 03:15, the job runs at the next boot instead of waiting a day.
RandomizedDelaySecStops several jobs from hitting the disk at exactly the same second.
IOSchedulingClass=idleThe 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.