Cron expressions are everywhere. They power database backups, CI/CD pipelines, batch jobs, and automated notifications across the web. Yet many developers treat them like black magic: you find an example that works, copy it, and hope it keeps working.
It doesn't have to be that way. Cron syntax is actually simple once you understand the five fields and the ranges they represent. This guide breaks down the visual structure so you can read, write, and debug cron expressions with confidence.
The Five-Field Structure
A standard cron expression has exactly five fields, separated by spaces:
MINUTE HOUR DAY MONTH DAY_OF_WEEK 0 9 * * *
Each field controls when a job runs. Here's what each one means:
- MINUTE: 0-59 (which minute of the hour)
- HOUR: 0-23 (which hour of the day, 24-hour format)
- DAY: 1-31 (which day of the month)
- MONTH: 1-12 (which month of the year)
- DAY_OF_WEEK: 0-6 (which day of the week, 0 = Sunday)
The asterisk (*) means "every" for that field. So 0 9 * * * means "at 9:00 AM every day, regardless of the month or day of the week."
Common Patterns You'll Actually Use
Most real-world cron jobs fall into a handful of patterns. Learn these and you'll cover 80% of use cases:
0 0 * * * # Midnight every day 0 9 * * 1-5 # 9 AM every weekday (Monday-Friday) 0 */6 * * * # Every 6 hours (0, 6, 12, 18) 30 2 * * 0 # 2:30 AM every Sunday 0 0 1 * * # Midnight on the first of each month 0 12 15 * * # Noon on the 15th of each month */5 * * * * # Every 5 minutes
Let's break down the slash operator: */5 in the minute field means "every 5 minutes" (0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55). The slash divides the range evenly.
For the day of the week, note that 0 and 7 both represent Sunday. So 1-5 is Monday through Friday, and 0-4 is Sunday through Thursday.
Operators and Syntax Rules
You have a few tools available to build more complex expressions:
- Asterisk (*): Any value
- Comma (,): Specific values.
9,14,21means hours 9, 14, and 21 - Hyphen (-): Ranges.
9-17means 9 through 17 inclusive - Slash (/): Step values.
*/15means every 15 units
You can combine operators. For example, 0 9-17 * * 1-5 runs at the top of every hour from 9 AM to 5 PM on weekdays. The hyphen creates the range 9, 10, 11, 12, 13, 14, 15, 16, 17. Then the job fires at minute 0 of each of those hours.
Common Mistakes
Even experienced developers stumble on these:
Mistake 1: Forgetting timezones. Cron always runs in the server's local timezone unless your cron daemon is configured otherwise. If your server is in UTC and you want 9 AM EST, you need to do the math: that's 14:00 UTC, so 0 14 * * *. Double-check this. It's the #1 reason scheduled jobs run at the wrong time.
Mistake 2: Conflicting DAY and DAY_OF_WEEK. If you specify both a day of month and a day of week (anything other than * in both), cron uses OR logic, not AND. For example, 0 9 15 * 5 runs at 9 AM on either the 15th of the month OR on Friday. It does not run only on Fridays that are the 15th. To avoid confusion, set one of them to *.
Mistake 3: Invalid ranges. Remember that months go 1-12, days go 1-31 (with some months having fewer), and day of week goes 0-6. Off-by-one errors here cause silent failures.
Mistake 4: Not accounting for job duration. If your cron job takes 30 minutes to run and you schedule it every 30 minutes, overlapping executions will pile up. Build in headroom, or use a locking mechanism to prevent concurrent runs.
Real-World Examples
Database backup at 2 AM daily: 0 2 * * *
Health check every 5 minutes during business hours: */5 9-17 * * 1-5
Weekly cleanup on Sunday at 3 AM: 0 3 * * 0
End-of-month report on the last day at 6 PM: This one is trickier. Standard cron can't natively express "last day of month," so you use a workaround: 0 18 28-31 * * and then in your script, check if tomorrow's date has a different month. Or use a cron extension if your system supports it.
Quarterly review on the 1st of Jan, Apr, Jul, Oct at noon: 0 12 1 1,4,7,10 *
Debugging Tools and Testing
Before deploying a cron job to production, verify it in a tool. Services like Crontab Guru provide instant feedback. You paste in your expression and it tells you exactly when your job will run for the next several occurrences. Use this every time. It takes 10 seconds and prevents hours of debugging.
In your own tools or applications, TerminalFeed now offers a Cron Expression Builder that decodes expressions visually and shows you the schedule in human-readable format. Try it when learning or when debugging existing cron jobs.
BACKUP_SCHEDULE=0 2 * * * # 2 AM daily is much clearer than discovering a bare 0 2 * * * buried in code months later and guessing what it does.
Beyond Basic Cron
Some systems support extended cron syntax with a sixth field for seconds, or allow named ranges like MON-FRI instead of 1-5. AWS EventBridge, for example, uses its own rate and cron expression syntax. Always check your platform's documentation. The five-field standard is portable across Linux, macOS, Unix, and many cloud platforms, but custom systems vary.
If you're building scheduled tasks in modern languages like Python, Node.js, or Go, consider libraries like APScheduler, node-cron, or cronv3 that provide more flexibility than system cron. They let you define schedules in code and handle timezone conversion automatically.
Want to build or test cron expressions visually?
Try the Cron ToolNext Steps
The best way to master cron is to write a few expressions, test them, and build intuition. Start with the common patterns section above. Build a backup job, a health check, a report scheduler. Each one teaches you something. And when you encounter an edge case, you'll know where to look.
Cron has been around since 1975 for a reason. It works, it's simple, and once you understand the five fields, you'll never see a cron expression as magic again.