Skip to content
AgileLogics
6 min read

Scheduling: cron and systemd Timers

Prerequisites: Your first script, Logging and user-friendly output

Automation often means running a script on a schedule — backups every night, reports every Monday, cleanup every hour.

cron

crontab -e opens your personal schedule. Each line is five time fields and a command:

# ┌───────────── minute (0–59)
# │ ┌─────────── hour (0–23)
# │ │ ┌───────── day of month (1–31)
# │ │ │ ┌─────── month (1–12)
# │ │ │ │ ┌───── day of week (0–7, Sunday = 0 or 7)
# │ │ │ │ │
  30 2 * * *   /home/ada/bin/backup.sh >> /home/ada/logs/backup.log 2>&1
  0 9 * * 1    /home/ada/bin/weekly-report.sh
  */15 * * * * /home/ada/bin/check-site.sh
  • 30 2 * * * — every day at 02:30.
  • 0 9 * * 1 — Mondays at 09:00.
  • */15 * * * * — every 15 minutes.

Sites such as crontab.guru explain any schedule in plain English.

Making scripts cron-friendly

cron runs commands with a minimal environment — a different PATH, no interactive shell settings, and a different working directory. Scripts that work in your terminal often fail under cron. So:

  • Use absolute paths for scripts, files, and commands (or set PATH at the top of the script).
  • cd to the right folder inside the script: cd "$(dirname "$0")" || exit 1.
  • Redirect output to a log file (>> file 2>&1); otherwise it's emailed to the user or lost.
  • Use a lock so a slow run doesn't overlap the next one (the Concurrency lesson's flock).
  • Exit non-zero on failure, and monitor for it (for example, with a "dead man's switch" service that alerts when a job stops reporting in).

systemd timers (Linux)

On modern Linux servers, systemd timers are an alternative with better logging (journalctl), dependency handling, and catch-up for missed runs:

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

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup nightly

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target
# illustrative
sudo systemctl enable --now backup.timer
systemctl list-timers
journalctl -u backup.service

Persistent=true runs a missed job after the machine was off at the scheduled time.

In the cloud

CI systems (GitHub Actions schedule:), Kubernetes CronJobs, and cloud schedulers run scripts on a schedule without a dedicated server.