The Five Fields
A crontab schedule has five fields separated by spaces:
* * * * * command
| | | | |
| | | | +-- Day of week (0-7, where 0 and 7 are Sunday)
| | | +---- Month (1-12)
| | +------ Day of month (1-31)
| +-------- Hour (0-23)
+---------- Minute (0-59)
Basic Patterns
Every Minute
* * * * * /path/to/script.sh
Runs every single minute. Useful for real-time monitoring but resource-intensive.
Every Hour
0 * * * * /path/to/script.sh
Runs at minute 0 of every hour.
Every Day at Midnight
0 0 * * * /path/to/script.sh
Every Monday at 9 AM
0 9 * * 1 /path/to/script.sh
First Day of Every Month at 6 AM
0 6 1 * * /path/to/script.sh
Special Characters
Comma: Multiple Values
# Run at 9 AM, 12 PM, and 5 PM
0 9,12,17 * * * /path/to/script.sh
Dash: Range
# Run every hour from 9 AM to 5 PM
0 9-17 * * * /path/to/script.sh
Slash: Step Values
# Run every 5 minutes
*/5 * * * * /path/to/script.sh
# Run every 2 hours
0 */2 * * * /path/to/script.sh
Real-World Examples
Database Backup - Daily at 2 AM
0 2 * * * /usr/local/bin/backup-db.sh
Log Rotation - Weekly on Sunday at 4 AM
0 4 * * 0 /usr/sbin/logrotate /etc/logrotate.conf
Health Check - Every 5 Minutes During Business Hours
*/5 9-17 * * 1-5 /path/to/health-check.sh
Monthly Report - First Monday of Every Month
This one is tricky. Cron does not natively support "first Monday." Use a wrapper:
# Run on the 1st through 7th, but only if it is Monday
0 9 1-7 * 1 /path/to/monthly-report.sh
Quarterly Cleanup - Jan, Apr, Jul, Oct
0 3 1 1,4,7,10 * /path/to/quarterly-cleanup.sh
Special Strings
Some cron implementations support shorthand strings:
@reboot Run once at startup
@yearly 0 0 1 1 *
@monthly 0 0 1 * *
@weekly 0 0 * * 0
@daily 0 0 * * *
@hourly 0 * * * *
Common Mistakes
Forgetting Timezone
Cron uses the system timezone. If your server is UTC but you want jobs to run at 9 AM local time, you need to calculate the UTC equivalent. Or set the timezone in crontab:
CRON_TZ=America/New_York
0 9 * * * /path/to/script.sh
Day-of-Month AND Day-of-Week
When both day-of-month and day-of-week are set (not *), cron runs the job when either matches, not when both match. This surprises many people.
# Runs on the 15th AND every Friday, not "Friday the 15th"
0 9 15 * 5 /path/to/script.sh
Minute Field Wildcard
# BAD: runs every minute of every hour
* */2 * * * /path/to/script.sh
# GOOD: runs once every 2 hours at minute 0
0 */2 * * * /path/to/script.sh
Testing Your Crontab
Before deploying, verify your schedule does what you think it does. Online tools like crontab.guru let you paste a cron expression and see the next execution times.
And regardless of how correct your syntax is, monitor your cron jobs to confirm they actually run. A perfectly written crontab on a server that lost its cron daemon does nothing at all.
Conclusion
Crontab syntax is simple but has sharp edges. The day-of-month/day-of-week OR behavior, timezone assumptions, and minute-field wildcards trip up even experienced engineers. Use shorthand strings when possible, test expressions before deploying, and always monitor that jobs actually execute.