Replacing cron jobs with systemd timers
Cron is fine until a job fails silently at 3 a.m. and you find out a week later. systemd timers need slightly more setup, but every run is logged in the journal, a run missed during downtime can be caught up, and jobs can depend on the network or a mounted disk.
Two files
A timer is a pair: a .service that does the work and a .timer that decides when.
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup of /srv
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Nice=10
IOSchedulingClass=idle
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup.service every night
[Timer]
OnCalendar=*-*-* 03:30:00
RandomizedDelaySec=15min
Persistent=true
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now backup.timer
Enable the timer, not the service. The service has no [Install] section on purpose.
The useful options
Persistent=true— if the machine was off at 03:30, the job runs once at the next boot.RandomizedDelaySec— spreads load when many machines share the same schedule.OnBootSec=/OnUnitActiveSec=— relative schedules, e.g. "10 minutes after boot, then every hour".AccuracySec=— defaults to one minute; lower it only if you really need precision.
Checking the schedule
Calendar expressions are easy to get wrong. systemd-analyze shows exactly when they will fire:
$ systemd-analyze calendar --iterations=3 'Mon..Fri 09:00'
Original form: Mon..Fri 09:00
Normalized form: Mon..Fri *-*-* 09:00:00
Next elapse: Thu 2026-03-12 09:00:00 UTC
Iter. #2: Fri 2026-03-13 09:00:00 UTC
Iter. #3: Mon 2026-03-16 09:00:00 UTC
And for everything that is scheduled on the machine:
systemctl list-timers --all
Logs and failures
journalctl -u backup.service --since yesterday
systemctl status backup.service
A failed run leaves the service in the failed state, which shows up in systemctl --failed and in most monitoring agents. To get notified, add OnFailure=notify@%n.service to the service and write a small template unit that sends a message.
When I still use cron
For a one-line job on a machine I do not maintain, or inside a minimal container without systemd, cron is still the simpler tool. Everywhere else, timers have replaced it for me.