Cron is the built-in Linux/macOS task scheduler that runs commands at exact times — without you having to remember. Once you know the five-field format, scheduling any recurring task takes under a minute.
Quick reference
| Expression | Meaning |
|---|---|
* * * * * |
Every minute |
0 * * * * |
Every hour (at :00) |
0 9 * * * |
Every day at 09:00 |
0 9 * * 1 |
Every Monday at 09:00 |
0 9 1 * * |
1st of every month at 09:00 |
0 9 1 1 * |
January 1st at 09:00 |
*/15 * * * * |
Every 15 minutes |
0 9,17 * * * |
At 09:00 and 17:00 daily |
0 9 * * 1-5 |
Weekdays at 09:00 |
0 0 * * 0 |
Every Sunday at midnight |
@reboot |
Once at system startup |
@daily |
Once a day at midnight |
The five-field format
Every cron expression has five fields separated by spaces, followed by the command to run:
┌─────────── minute (0–59)
│ ┌────────── hour (0–23)
│ │ ┌─────── day of month (1–31)
│ │ │ ┌──── month (1–12)
│ │ │ │ ┌─ day of week (0–7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * * command-to-run
Field values
| Field | Allowed values | Special chars |
|---|---|---|
| Minute | 0–59 | * , - / |
| Hour | 0–23 | * , - / |
| Day of month | 1–31 | * , - / ? L |
| Month | 1–12 (or JAN–DEC) |
* , - / |
| Day of week | 0–7 (0 and 7 = Sunday, or SUN–SAT) |
* , - / ? L |
Special characters
| Char | Meaning | Example |
|---|---|---|
* |
Any value | * * * * * — every minute |
, |
List of values | 0 9,13,17 * * * — 09:00, 13:00, 17:00 |
- |
Range | 0 9 * * 1-5 — Monday through Friday |
/ |
Step (every N) | */15 * * * * — every 15 minutes |
L |
Last | 0 9 L * * — last day of month at 09:00 |
? |
No specific value | Used in day fields when specifying the other |
Special strings (@shortcuts)
These replace the five fields entirely:
| String | Equivalent | Meaning |
|---|---|---|
@reboot |
— | Run once at startup |
@yearly / @annually |
0 0 1 1 * |
Once a year, Jan 1 at midnight |
@monthly |
0 0 1 * * |
Once a month, 1st at midnight |
@weekly |
0 0 * * 0 |
Once a week, Sunday at midnight |
@daily / @midnight |
0 0 * * * |
Once a day at midnight |
@hourly |
0 * * * * |
Once an hour at :00 |
@reboot /home/ubuntu/start-app.sh
@daily /usr/local/bin/db-backup.sh
Crontab commands
Edit your crontab
crontab -e # edit your crontab (opens $EDITOR)
crontab -l # list current cron jobs
crontab -r # remove all your cron jobs (careful!)
crontab -u alice -e # edit another user's crontab (root only)
System-wide cron files
/etc/crontab # system crontab (has extra "user" field)
/etc/cron.d/ # drop-in cron files (same format as /etc/crontab)
/etc/cron.daily/ # scripts run daily (no cron syntax needed)
/etc/cron.hourly/ # scripts run hourly
/etc/cron.weekly/ # scripts run weekly
/etc/cron.monthly/ # scripts run monthly
The system /etc/crontab format includes a username field:
# /etc/crontab
0 2 * * * root /usr/local/bin/nightly-backup.sh
Practical scheduling examples
Backups
# Database backup every night at 2:00 AM
0 2 * * * /usr/local/bin/db-backup.sh >> /var/log/backup.log 2>&1
# Weekly backup every Sunday at 3:30 AM
30 3 * * 0 /usr/local/bin/weekly-backup.sh
# Monthly archive on the 1st at midnight
0 0 1 * * tar -czf /backups/monthly-$(date +\%Y\%m).tar.gz /var/data
Log rotation / cleanup
# Delete log files older than 30 days, every night at 1 AM
0 1 * * * find /var/log/myapp -name "*.log" -mtime +30 -delete
# Truncate a log file every Monday morning
0 6 * * 1 > /var/log/myapp/debug.log
Application tasks
# Send a daily digest email at 8 AM
0 8 * * * /usr/bin/python3 /opt/myapp/send_digest.py
# Clear cache every 6 hours
0 */6 * * * redis-cli FLUSHDB
# Re-index search every Sunday at 4 AM
0 4 * * 0 curl -X POST https://api.example.com/reindex
# Run a Node.js script every 5 minutes
*/5 * * * * /usr/bin/node /home/ubuntu/scripts/poll.js >> /home/ubuntu/logs/poll.log 2>&1
System maintenance
# Sync time at midnight
0 0 * * * ntpdate pool.ntp.org
# Check disk space and alert if over 80%
*/30 * * * * df -h | awk '$5 > 80 {print}' | mail -s "Disk Alert" admin@example.com
# Start a service after reboot
@reboot /usr/sbin/nginx
Output and logging
By default, cron emails you the output. To redirect it:
# Redirect both stdout and stderr to a log file
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Discard all output silently
0 2 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
# Append timestamp to log
0 2 * * * echo "$(date): starting backup" >> /var/log/backup.log && /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Email output to a specific address (set MAILTO)
MAILTO=ops@example.com
0 2 * * * /usr/local/bin/backup.sh
Environment variables in crontab
Cron runs with a minimal environment — not your login shell. The PATH, HOME, and other variables you rely on may not be set.
# At the top of your crontab
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=ops@example.com
HOME=/home/ubuntu
# Or use absolute paths in every command
0 2 * * * /usr/bin/python3 /opt/app/script.py
Always use absolute paths for both the command and any files it references.
Escaping percent signs
The % character in a cron command is treated as a newline — it must be escaped with \%:
# Wrong — % is interpreted as newline
0 0 * * * tar -czf /backups/backup-$(date +%Y-%m-%d).tar.gz /var/data
# Correct — escape %
0 0 * * * tar -czf /backups/backup-$(date +\%Y-\%m-\%d).tar.gz /var/data
Debugging cron jobs
Check if cron is running
systemctl status cron # Debian/Ubuntu
systemctl status crond # RHEL/CentOS/Fedora
ps aux | grep cron
Check the cron log
grep CRON /var/log/syslog # Ubuntu/Debian
grep CRON /var/log/cron # RHEL/CentOS
journalctl -u cron # systemd
journalctl -u cron | tail -50 # recent entries
Test your command manually
Always run the exact command from crontab manually in a fresh shell before scheduling it:
env -i HOME=/home/ubuntu PATH=/usr/local/bin:/usr/bin:/bin /bin/bash -c "your-command"
This simulates the minimal cron environment.
Common debugging checklist
- Absolute paths — is every path in the command absolute?
- Permissions — does the cron user have execute permission on the script?
- Script shebang — does your script start with
#!/bin/bash(or the correct interpreter)? - Line endings — scripts created on Windows have
\r\nline endings that break Linux. Fix withdos2unix script.sh. - Output logged — redirect
2>&1so errors appear in your log, not just stdout. - MAILTO set — set
MAILTO=""to suppress emails if your mail isn't configured.
Common mistakes
| Mistake | Why it breaks | Fix |
|---|---|---|
| Relative paths | Cron's $PATH is minimal |
Use /usr/bin/python3, not python3 |
Unescaped % |
Cron treats % as newline |
Replace % with \% |
| Wrong day-of-week values | Some crons treat 7 as Sunday, others don't | Use 0 for Sunday; 1–7 or 0–6 work safely |
Editing /etc/crontab directly without user field |
System crontab needs a username | Add username: 0 2 * * * root /script.sh |
| Assuming login environment | Cron has no .bashrc or .profile |
Set PATH explicitly at top of crontab |
| Both day-of-month and day-of-week set | Cron uses OR logic (not AND) | Set one to *; if both are non-*, either match triggers |
| No logging | Hard to debug silently failing jobs | Always redirect: >> /var/log/job.log 2>&1 |
Quick reference: timing patterns
| Goal | Expression |
|---|---|
| Every minute | * * * * * |
| Every 5 minutes | */5 * * * * |
| Every 15 minutes | */15 * * * * |
| Every 30 minutes | */30 * * * * |
| Every hour | 0 * * * * |
| Every 2 hours | 0 */2 * * * |
| Every day at midnight | 0 0 * * * |
| Every day at 6 AM | 0 6 * * * |
| Every weekday at 9 AM | 0 9 * * 1-5 |
| Every weekend at noon | 0 12 * * 6,0 |
| Every Monday at 8 AM | 0 8 * * 1 |
| First day of month at midnight | 0 0 1 * * |
| Last day of month at 11 PM | 0 23 L * * |
| Every 3 months (Jan/Apr/Jul/Oct) | 0 0 1 */3 * |
| Once a year (Jan 1 at midnight) | 0 0 1 1 * |
| At reboot | @reboot |
FAQ
What timezone does cron use?
Cron uses the system timezone by default. To use a different timezone, set TZ=America/New_York at the top of your crontab. Check the system timezone with timedatectl or date.
Does cron run if the system was off at the scheduled time?
Standard cron does not catch up on missed jobs. If your system was off at 2 AM, the 2 AM job simply doesn't run. Use anacron (or systemd timers with Persistent=true) if you need catch-up behaviour.
Why do my cron jobs work manually but not in cron?
Almost always an environment issue — missing PATH, relative paths, or shell differences. Test with env -i ... /bin/bash -c "command" to simulate cron's environment.
How do I run a cron job as a different user?
Edit the system crontab (/etc/crontab or a file in /etc/cron.d/) and include the username:
0 2 * * * deploy /usr/local/bin/deploy.sh
Can I run cron more often than once a minute?
Standard cron's resolution is one minute. For sub-minute scheduling, use a loop inside the cron job (while true; do ...; sleep 10; done) or switch to systemd timers.
How do I verify a cron expression before using it? Use an online cron validator or test the job manually. The Unix Timestamp tool helps you verify exact run times by converting between human-readable dates and timestamps.