What you will achieve
Pick between cron and systemd timers for scheduled jobs — logging, randomised delays, and dependency awareness on modern Debian/Ubuntu.
1) Classic cron
crontab -e
# system-wide: /etc/cron.d/ or /etc/cron.daily/
# Example: daily backup at 02:15
15 2 * * * /usr/local/bin/backup.sh
2) systemd timer
# /etc/systemd/system/backup.service
[Unit]
Description=Backup job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup
[Timer]
OnCalendar=*-*-* 02:15:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl enable --now backup.timer
systemctl list-timers
3) When timers win
- Jobs need
After=network-online.target. - You want journald logs via
journalctl -u backup.service. - Missed runs should catch up (
Persistent=true).
Verify
systemctl status backup.timer
grep CRON /var/log/syslog | tail
5) Randomised delay (systemd)
[Timer]
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=900
Prevents thousand hosts hitting API at identical second.
6) anacron for laptops
/etc/cron.daily on Debian uses anacron — jobs run when machine was off at scheduled time. systemd Persistent=true solves same problem on systemd-only paths.
7) Environment variables
# cron struggles with minimal PATH
PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * /usr/local/bin/backup.sh
systemd service units set Environment= explicitly — clearer than cron hacks.
Migration tip
Convert critical cron jobs to timers one at a time. Run both in parallel briefly and compare journald output vs cron mail.
8) daylight saving gotchas
Cron local time jumps on DST — systemd calendar expressions handle transitions more predictably for wall-clock schedules.
Prerequisites
Script tested manually as correct user. Timezone set (timedatectl). Mail or logging for job output. Root for system timers in /etc/systemd/system/.
cron MAILTO
MAILTO=admin@example.com
0 4 * * * /usr/local/bin/backup.sh
Requires working mail — otherwise cron silently drops output unless redirected to log file.
systemd timer OnBootSec
OnBootSec=15minRun job 15 minutes after boot — handy for laptop that misses OnCalendar while asleep.
Overlap protection
systemd OnUnitActiveSec= prevents pile-up if job runs longer than interval — cron fires regardless and stacks duplicate backups. Use flock -n /var/lock/backup.lock in cron scripts for same protection on legacy cron paths.
anacron on servers always on
anacron redundant when systemd Persistent timers exist — pick one maintenance path. Mixed cron.daily and systemd timer duplicating same backup wastes I/O and confuses on-call about which log to read.
Timezone explicit
systemd OnCalendar uses local timezone unless UTC specified — document whether backup runs 02:00 Europe/London or UTC to avoid daylight saving surprises for global teams.