What Is a Cron Expression?
A cron expression is a string of five or six fields that defines a schedule. Originally born in Unix's cron daemon (circa 1975), it has become the universal language for task scheduling across Linux, Kubernetes, Spring, Node.js, and cloud platforms.
The classic five-field format covers everything from "run every minute" to "run at 3:17 AM on the second Friday of March." Once you understand the syntax, you can express virtually any recurring schedule in a single line.
The Five Fields, Broken Down
Each cron expression consists of five space-separated fields:
| Field | Values | What It Controls |
|---|---|---|
| Minute | 0–59 | Which minute of the hour |
| Hour | 0–23 | Which hour of the day |
| Day of Month | 1–31 | Which day of the month |
| Month | 1–12 | Which month |
| Day of Week | 0–6 (Sun–Sat) | Which day of the week |
Special characters tie these fields together: * (any value), , (list), - (range), / (step), and ? (no specific value, used in some implementations).
Common Patterns You'll Actually Use
Here are the cron expressions that cover 90% of real-world scheduling needs:
# Every minute
* * * * *
# Every 5 minutes
*/5 * * * *
# Hourly, at minute 0
0 * * * *
# Daily at 2:30 AM
30 2 * * *
# Every Monday at 9 AM
0 9 * * 1
# Weekdays at 8 AM and 6 PM
0 8,18 * * 1-5
# First day of every month at midnight
0 0 1 * *
# Every 3 hours during business hours
0 9-17/3 * * 1-5
Day-of-Month vs Day-of-Week: The Classic Trap
The single most common cron mistake: specifying both day-of-month (field 3) and day-of-week (field 5). In standard Linux cron, both conditions are OR'd, not AND'd. So 0 0 15 * 1 means "run at midnight on the 15th OR any Monday" — not "run at midnight on Monday the 15th."
To target a specific day-of-month on a specific weekday, you need a test in the command itself:
# Run only if it's Monday AND the 15th
0 0 15 * 1 test "$(date +\%a)" = "Mon" && /path/to/script.sh
This quirk has burned developers for decades. Always verify your schedule logic in a [cron expression builder](/tools/cron-expression-generator.html) before deploying to production.
Six-Field Variants
Some systems extend the format. Kubernetes CronJobs prepend seconds (0 30 2 * * * for 2:30:00 AM). Spring Framework supports both five and six fields plus optional year. Always check your platform's documentation — a five-field expression pasted into a six-field system will shift every field left by one, producing completely wrong schedules.
Real-World Scheduling Scenarios
Database Backups with Rotation
# Full backup every Sunday at 3 AM
0 3 * * 0 /usr/bin/pg_dump -Fc mydb > /backup/full_$(date +\%F).dump
# Incremental backup weekdays at 3 AM
0 3 * * 1-5 /usr/bin/pg_dump --section=data mydb > /backup/inc_$(date +\%F).dump
# Clean backups older than 30 days, daily at 4 AM
0 4 * * * find /backup -name "*.dump" -mtime +30 -delete
API Health Checks
# Check every 2 minutes, alert on failure
*/2 * * * * curl -sf https://api.example.com/health || \
curl -X POST https://hooks.slack.com/... -d '{"text":"API down"}'
Log Rotation and Cleanup
# Rotate nginx logs daily at midnight
0 0 * * * /usr/sbin/logrotate /etc/logrotate.d/nginx
# Compress logs older than 7 days
0 1 * * * find /var/log/app -name "*.log" -mtime +7 -exec gzip {} \;
Debugging Cron Jobs: A Checklist
When a cron job silently fails (and they always fail silently), work through these steps:
- Environment variables: Cron runs with a minimal environment. Your
$PATH,$HOME, and other variables may differ from your shell. Use absolute paths or source your profile explicitly. - Check syslog:
grep CRON /var/log/syslogshows every cron execution and any errors. - Redirect output: Append
> /tmp/cron.log 2>&1to capture both stdout and stderr. - Timezone mismatch: Cron uses the system timezone. A server set to UTC will execute
0 9 * * *at 9 AM UTC, not your local time. Usetimedatectlto verify. - Line endings: If you edit crontab on Windows and transfer to Linux,
\r\nline endings will break parsing. Rundos2unixon the file.
Cron in Modern Infrastructure
While traditional crontab -e still works, modern deployment patterns often use different wrappers:
- Kubernetes CronJob: Adds
concurrencyPolicy,startingDeadlineSeconds, andsuccessfulJobsHistoryLimit. Remember the six-field format with seconds. - Node.js (node-cron): Uses the same five-field syntax but runs in your application process — useful for per-instance scheduling without system-level access.
- GitHub Actions: Uses
on.schedule.cronwith UTC timezone. Expressions are five-field but limited to a minimum interval of 5 minutes. - CloudWatch Events / EventBridge: AWS uses a slightly different expression syntax — not standard cron. Always consult the AWS docs.
Build Expressions Visually
If you're tired of counting asterisks, try a visual cron expression builder. Tools like [RiseTop's cron generator](/tools/cron-expression-generator.html) let you select schedule parameters through dropdowns and checkboxes, then output the correct expression for your platform. It handles the five-field and six-field formats and shows human-readable descriptions like "Every weekday at 9:00 AM."
Key Takeaways
- Cron's five-field syntax is powerful but has quirks — especially the day-of-month OR day-of-week behavior.
- Always use absolute paths in cron commands, and redirect output to a log file for debugging.
- Different platforms (K8s, Spring, GitHub Actions) have subtle syntax variations — never assume interchangeability.
- Use a visual builder to construct complex expressions, then validate by reading the generated description back.