Imagine your computer doing your chores while you sleep: backing up files at 2 AM, cleaning out junk every Sunday, emailing you a report before your morning coffee. That's not science fiction — it's cron, the job scheduler that's been quietly running on Linux and Unix systems for decades.
If you're getting into automation, cron is one of the highest-leverage tools you can learn. It takes about ten minutes, and this guide covers everything you need: the syntax, the safe way to edit jobs, logging, the classic beginner pitfalls, and real-world examples you can steal.
What is cron, really?
Cron is a background service (a "daemon") that wakes up every minute, checks a list of scheduled tasks, and runs whichever ones are due. Each user on a Linux system has their own list, called a crontab (cron table).
A single line in your crontab is a single scheduled job: when to run it, followed by what to run. That's the whole mental model.
Cron vs. the alternatives
Cron isn't the only scheduler out there. Here's when to reach for it — and when not to:
- Use cron for recurring tasks on a Linux server or desktop: backups, cleanups, report generation, syncing files.
at— for one-off jobs ("run this once, tomorrow at noon"). Cron is for repeating schedules.- systemd timers — the modern Linux alternative with more features (dependencies, randomized delays). More powerful, but more verbose to set up.
- Task Scheduler / launchd — the native equivalents on Windows and macOS.
- Cloud schedulers (AWS EventBridge, GCP Cloud Scheduler) — when your workload already lives in the cloud.
For 90% of personal automation on Linux, cron wins on simplicity. Let's learn it.
The 5 fields, decoded
Every cron line starts with five time fields, then the command:
* * * * * command to run
│ │ │ │ │
│ │ │ │ └─── Day of week (0–7, where both 0 and 7 mean Sunday)
│ │ │ └───── Month (1–12)
│ │ └─────── Day of month (1–31)
│ └───────── Hour (0–23)
└─────────── Minute (0–59)
Read it left to right, smallest unit to largest. Each field accepts a number, a range (1-5), a step (*/15), a list (1,15), or * meaning "every".
| Field | Allowed values |
|---|---|
| Minute | 0–59 |
| Hour | 0–23 (24-hour clock, so 2 PM is 14) |
| Day of month | 1–31 |
| Month | 1–12, or names like jan, feb |
| Day of week | 0–7 (0 and 7 are both Sunday), or names like mon, tue |
Two handy shortcuts inside fields: */5 means "every 5" (0, 5, 10, …), and 9-17 means "from 9 through 17". You can combine them: */15 9-17 is every 15 minutes between 9 AM and 5 PM.
Schedules you'll actually use
Here are the patterns that cover nearly every real need. Each one has been checked for correctness:
Every 5 minutes
*/5 * * * * /home/greg/scripts/check.sh
Every day at 2:00 AM
0 2 * * * /home/greg/scripts/nightly-backup.sh
Every Monday at 9:00 AM
0 9 * * 1 /home/greg/scripts/weekly-report.sh
First day of every month, at midnight
0 0 1 * * /home/greg/scripts/monthly-cleanup.sh
Every 15 minutes, 9 AM–5 PM, Monday to Friday
*/15 9-17 * * 1-5 /home/greg/scripts/sync.sh
Once a day at midnight (special shortcut)
@daily /home/greg/scripts/rotate-logs.sh
Cron also understands a few @ shortcuts so you don't have to memorize the fields:
| Shortcut | Equivalent schedule |
|---|---|
@hourly |
At the top of every hour |
@daily / @midnight |
Once a day at midnight |
@weekly |
Once a week, Sunday at midnight |
@monthly |
Once a month, on the 1st at midnight |
@reboot |
Once, every time the system boots |
Editing your crontab safely
Never edit the crontab file directly — use the crontab command, which validates your syntax before saving:
crontab -e # edit your crontab (opens your default editor)
crontab -l # list your current jobs
crontab -r # REMOVE your entire crontab — careful, no undo!
A few safety habits:
- Always list before you edit. Run
crontab -lfirst so you know what's already there. - Pick your editor. If
crontab -eopens something unfamiliar, set your preference first:EDITOR=nano crontab -e. - Comment generously. Lines starting with
#are ignored — future-you will thank present-you for noting what each job does. - Test the command in your terminal first. If it doesn't work when you type it, it won't work from cron either.
Logging: don't fly blind
By default, a cron job's output goes… nowhere useful. (Technically cron tries to email it to you, which almost nobody has configured.) Always redirect output to a log file:
0 2 * * * /home/greg/scripts/nightly-backup.sh >> /home/greg/logs/backup.log 2>&1
The >> appends normal output to the log, and 2>&1 funnels error messages into the same file. When something breaks at 2 AM, this log is how you'll figure out why.
If you'd rather cron didn't even try to send email, add this line at the top of your crontab:
MAILTO=""
4 pitfalls that bite every beginner
1. Cron's environment is nearly empty
Your cron jobs do not get your shell's PATH, aliases, or custom variables. A command that works in your terminal can fail in cron simply because it can't find the program. Two fixes:
- Use absolute paths for everything (
/usr/bin/python3, notpython3). - Or set PATH at the top of your crontab:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
2. The % character is special
In a crontab command, an unescaped % is converted to a newline — which silently mangles commands like $(date +%F). Escape it with a backslash:
# WRONG — the % breaks the command
0 2 * * * tar -czf backup-$(date +%F).tar.gz /data
# RIGHT — escape the %
0 2 * * * tar -czf backup-$(date +\%F).tar.gz /data
3. Jobs can overlap themselves
If a job takes longer than its interval (a 10-minute sync running every 5 minutes), you'll get multiple copies piling up. Guard with flock, which skips the run if the previous one is still going:
*/5 * * * * /usr/bin/flock -n /tmp/sync.lock /home/greg/scripts/sync.sh
4. Cron runs on system time — silently
Cron follows the machine's local timezone, not yours. If your server is on UTC and you're on Eastern, "2 AM" means 2 AM UTC. Check with timedatectl (Linux) and adjust your hours accordingly.
3 real-world examples
Example 1: Nightly website backup
Compress the web directory every night at 2 AM, with the date in the filename, and log the result:
0 2 * * * /usr/bin/tar -czf /home/greg/backups/website-$(date +\%F).tar.gz -C /var/www html >> /home/greg/backups/backup.log 2>&1
Note the absolute path to tar, the escaped \%, and the log redirect — all three lessons from above in one line.
Example 2: Weekly temp-file cleanup
Delete cached files older than 7 days every Sunday at 3:30 AM:
30 3 * * 0 /usr/bin/find /tmp/myapp-cache -type f -mtime +7 -delete
The -delete flag removes matches directly — test with -print instead of -delete first to preview what would be removed.
Example 3: A Python script on a schedule
Run a morning report script at 6 AM daily:
0 6 * * * /usr/bin/python3 /home/greg/scripts/morning_report.py >> /home/greg/scripts/report.log 2>&1
Inside the script, don't rely on the current working directory — use absolute paths for any files it reads or writes, since cron launches it from your home directory, not the script's folder.
Your 10-minute checklist
- ☐ Run
crontab -lto see what's already scheduled - ☐ Open the editor with
EDITOR=nano crontab -e - ☐ Add one job using absolute paths and a log redirect
- ☐ Escape any
%characters as\% - ☐ Save, then run
crontab -lto confirm it's there - ☐ Check your log file after the first run
That's cron. Five fields, one command, and your computer starts handling the boring stuff on its own. Start with one nightly job this week — by next month you'll wonder how you ever did it manually.

Join the discussion