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
PATHat the top of the script). cdto 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.