You will schedule a recurring server task with both cron and a systemd timer, adding logging and missed-run catch-up. By the end you will have observable automation that survives reboots and tells you when it fails.

Learning Objectives

  • Write a logged cron entry with a sane environment.
  • Create an equivalent systemd timer with Persistent catch-up.
  • Alert on failure rather than failing silently.
  • Time: ~2 hours · Difficulty: Beginner · Prereqs: a systemd-based server.

Architecture Overview

Environment Setup

You will need: a systemd server and a script to schedule (for example the restic backup from the backup lab).

Before you begin: use absolute paths in scheduled jobs; cron's environment is minimal.

Step-by-Step Execution

01
Add a logged cron entry

Redirecting both streams to a log makes cron failures visible instead of silent.

( crontab -l 2>/dev/null; echo "30 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1" ) | crontab -
02
Create a systemd timer with catch-up

Persistent=true runs a missed job after downtime, which plain cron will not do.

printf '[Timer]\nOnCalendar=*-*-* 02:30:00\nPersistent=true\n[Install]\nWantedBy=timers.target\n' | sudo tee /etc/systemd/system/backup.timer
[ROOT REQUIRED] Pairs with a backup.service unit.
03
Enable and inspect
# systemctl daemon-reload && systemctl enable --now backup.timer && systemctl list-timers backup.timer
NEXT LEFT UNIT ACTIVATES 2026-06-18 02:30 UTC 16h backup.timer backup.service

Progress So Far

Testing & Validation

sudo systemctl start backup.service && journalctl -u backup.service -n 5 --no-pager

You should see the job run and log output. If the timer also appears in list-timers with a future run, scheduling is working.

Troubleshooting
  • Cron job runs in shell but not scheduled: cron has a minimal PATH; use absolute paths and set variables.
  • Timer never fires: confirm it is enabled and the OnCalendar syntax with systemd-analyze calendar.
  • Missed runs: set Persistent=true so the job catches up after downtime.

Extension Ideas

  • Add an OnFailure= unit that sends a notification.
  • Schedule the restic backups from the backup lab.
  • Randomize start with RandomizedDelaySec to avoid thundering herds.

Key Results

  • Scheduled a task via both cron and a systemd timer.
  • Captured all job output to logs for observability.
  • Enabled missed-run catch-up with Persistent timers.
  • Validated the schedule with list-timers and a manual run.