Cron Jobs 101: Scheduling Tasks on Linux

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.