Cron has been Unix’s task scheduler since the 1970s, and despite systemd timers offering a more modern alternative, its five-field syntax remains the universal language of “run this thing periodically” across every Linux and macOS system you’ll ever touch.
Reading Crontab Syntax
# minute hour day-of-month month day-of-week command # * * * * * # | | | | | # | | | | +-- 0-6 (Sunday=0) # | | | +-------- 1-12 # | | +--------------------- 1-31 # | +-------------------------- 0-23 # +--------------------------------- 0-59 0 2 * * * /usr/local/bin/backup.sh # every day at 2:00 AM */15 * * * * /usr/local/bin/healthcheck.sh # every 15 minutes 0 9 * * 1-5 /usr/local/bin/weekday-report.sh # 9 AM on weekdays only 0 0 1 * * /usr/local/bin/monthly-cleanup.sh # midnight on the 1st
Editing and Managing Crontabs
crontab -e # edit your own crontab crontab -l # list current cron jobs sudo crontab -u www-data -e # edit another user's crontab # System-wide jobs (with a user field) live in /etc/crontab # and drop-in files under /etc/cron.d/
The #1 Beginner Mistake: Missing Environment
Cron jobs run with a minimal environment — no $PATH customizations, no shell profile loaded. A script that works perfectly in your interactive terminal often fails silently under cron because it can’t find a binary. Always use absolute paths, and redirect output somewhere you’ll actually see it:
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
When to Use systemd Timers Instead
Systemd timers offer better logging (via journald), dependency management, and the ability to catch up on missed runs after a machine was off (Persistent=true) — genuinely worth the extra setup for anything important, while cron remains perfectly fine for quick, low-stakes personal scripts.