I scheduled recurring tasks with cron and systemd timers, handling cron's minimal environment, logging, and failure notification. Capturing output and using absolute paths eliminated the classic "works in my shell but not in cron" failures.

Objective & Context

Scheduling is how routine work becomes hands-off. This lab covers crontab syntax, one-shot at jobs, and systemd timers (with logging and dependency control), the scheduling layer behind backups, health checks, and the vulnerability-scan automation.

Environment & Prerequisites

  • Linux with cron and systemd.
  • A script to schedule (backup or health check).
  • A mail or webhook channel for failure alerts.

Step-by-Step Execution

1. Add a logged cron job

(crontab -l; echo "0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1") | crontab -

2. Equivalent systemd timer

systemctl enable --now backup.timer && systemctl list-timers backup.timer

3. One-shot job with at

echo '/usr/local/bin/report.sh' | at 17:00
NEXT                     UNIT          ACTIVATES
2026-06-18 02:00 UTC     backup.timer  backup.service

Validation & Testing

Schedule a job a minute out, confirm it runs and logs, and force a failure to confirm the alert fires. Pass criteria: the job runs on schedule with full output captured, and non-zero exits trigger notification.

Advanced: Troubleshooting
  • Job runs in shell, not cron: cron has a minimal PATH/env; use absolute paths and set variables.
  • No output: redirect both streams (>> log 2>&1) to capture errors.
  • Timer vs cron: prefer systemd timers for logging, dependencies, and missed-run catch-up.

Key Results

  • Automated recurring tasks with logged cron and systemd timers.
  • Eliminated cron-environment failures via absolute paths.
  • Captured full job output for auditing and debugging.
  • Alerted on non-zero exits rather than failing silently.