Every developer who has set up a scheduled task has stared at a cron expression and questioned their career choices. 0 */4 * * 1-5 looks like line noise until you understand the five-field structure, and then it becomes one of the most elegant scheduling syntaxes ever designed.
The problem is not cron. The problem is that every explanation starts with the syntax instead of the mental model.
The mental model
A cron expression answers one question: "Which minutes of which hours of which days should this run?"
There are five fields, left to right:
- 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, Sunday=0): Which day of the week
An asterisk means "every." A number means "only this one." A comma separates multiple values. A hyphen defines a range. A slash defines a step.
That is the entire syntax. Everything else is combinations of these rules.
Building expressions by example
Every day at 3:00 AM: 0 3 * * *
Minute 0 of hour 3, every day, every month, every day of week.
Every Monday at 9:30 AM: 30 9 * * 1
Minute 30 of hour 9, every day of month, every month, only Monday.
Every 15 minutes: */15 * * * *
Every 15th minute starting from 0, every hour, every day.
Weekdays at 8 AM and 5 PM: 0 8,17 * * 1-5
Minute 0 of hours 8 and 17, only Monday through Friday.
First day of every month at midnight: 0 0 1 * *
Minute 0, hour 0, day 1, every month.
Every 6 hours: 0 */6 * * *
Minute 0, every 6th hour (0, 6, 12, 18).
The gotchas
Day of month vs day of week: When both are specified (not *), they create an OR condition, not AND. 0 0 15 * 5 runs on the 15th of every month AND every Friday, not "the 15th if it is a Friday." This catches people constantly.
*/5 vs 5: */5 in the minute field means every 5 minutes (0, 5, 10, 15...). 5 means only minute 5. A subtle but critical difference.
Month and day-of-week numbering: Months are 1-12 (January=1). Day of week is 0-6 (Sunday=0) in standard cron, but some implementations use 1-7 (Monday=1). Check your system's documentation.
No seconds field: Standard cron does not support seconds. If you need sub-minute scheduling, you need a different tool (systemd timers, or running a cron job every minute with a seconds check inside the script).
Timezone: Cron jobs run in the system's timezone by default. If your server is in UTC but you want a job to run at 9 AM Eastern, you need to account for the offset (and daylight saving time changes).
Cron in modern infrastructure
Classic crontab is still everywhere, but modern platforms have their own cron implementations:
-
GitHub Actions: Uses standard cron syntax in the
scheduletrigger - AWS CloudWatch Events/EventBridge: Standard cron with an optional seconds field and a year field
- Kubernetes CronJobs: Standard five-field cron syntax
- Vercel/Netlify cron: Standard syntax with platform-specific limitations on minimum frequency
The syntax transfers across all of these, which is why learning cron properly pays dividends across your entire infrastructure.
Building and validating expressions
The fastest way to build a cron expression is to use a generator that shows you the next several execution times. If the pattern matches what you expect, the expression is correct. I built a cron generator that lets you build expressions visually and see the next 10 scheduled runs, so you can verify before deploying.
I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.
Top comments (0)