tuxpy

Stop reaching for cron: systemd timers in practice

2026-04-02 · ~7 min

Cron is fine until you need to answer "did it run, and what happened?" Then you're tailing a mail spool or piping output to a logfile you'll forget to rotate. systemd timers give you the scheduling plus the logging, the dependency graph, and a record of every invocation — for free.

The two files

A timer is two units: the thing to run, and when to run it. The service:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup to offsite

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Nice=10
IOSchedulingClass=idle

And the timer:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run nightly backup

[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
RandomizedDelaySec=15m

[Install]
WantedBy=timers.target

Persistent=true is the line cron can't match: miss the window because the box was off, and the job runs once at next boot instead of silently skipping. RandomizedDelaySec spreads load if you have a fleet all firing at 03:30.

Then you actually enable it

systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers --all

And when something breaks, the answer is one command, with timestamps, scoped to exactly that job:

journalctl -u backup.service --since yesterday

No mail spool, no redirected logfile, no wondering whether it ran. That's the whole pitch.