systemd timers instead of cron
Cron works. systemd timers do things cron will never do.
Cron has one enormous advantage: everyone knows it. And one disadvantage: when it fails, you find out by email to root@localhost, which nobody reads.
systemd timers fix exactly that.
The basics
A timer is two files — .service (what runs) and .timer (when).
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup nightly
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15m
Persistent=true
[Install]
WantedBy=timers.target
What cron can’t do
Persistent=true — if the machine was off at the scheduled time, the job runs at next boot. Cron just skips it.
RandomizedDelaySec — spreads load when fifty machines would otherwise all fire at 02:30 exactly.
Logs in one place. journalctl -u backup.service shows the last run and the history. No redirecting to files.
Dependencies. After=network-online.target means the job won’t start before the network is up. In cron you solve that with sleep 30 and hope.
Resource limits. MemoryMax=, CPUQuota=, IOWeight= — cron has nothing like it.
Monitoring
This is the main reason we switched. Prometheus node_exporter collects systemd unit state, so a failed job raises an alert like anything else:
node_systemd_unit_state{name="backup.service",state="failed"} == 1
No MAILTO=, no log parsing. A failed job is just a metric.