Cron syntax explained — every position, every operator
Cron is the standard Unix scheduler. Used in server administration, automation, scheduled jobs in CI/CD, and many SaaS platforms. The syntax has 5 fields, a few operators, and some surprising edge cases. Once you understand it, you can express any periodic schedule.
For visual cron expression building and conversion, the cron builder generates expressions for you.
The 5 fields
A standard cron expression has 5 fields, separated by whitespace:
```
*
| | | | |
| | | | +--- Day of week (0-7, where 0 and 7 = Sunday)
| | | +----- Month (1-12)
| | +------- Day of month (1-31)
| +--------- Hour (0-23)
+----------- Minute (0-59)
```
Each field can be:
- A specific value: `5`, `15`, `1`, `12`
- A range: `1-5`, `9-17`
- A list: `1,15,30`
- A wildcard: `*` (any value)
- A step: `*/5` (every 5)
- A combination: `1-30/5` (every 5 between 1 and 30)
Some implementations support a 6th field for seconds (Spring/Quartz, certain modern cron implementations). Standard Unix cron is 5 fields.
Reading examples
```
0 Every hour, on the hour
0 0 * Every day at midnight
0 9 * Every day at 9 AM
0 9 1-5 Every weekday at 9 AM
/15 * Every 15 minutes
0 0 1 Midnight on the 1st of every month
0 0 1 1 * Midnight on January 1st
0 0 1 Midnight every Monday
0 22 5 10 PM every Friday
30 6 1,3,5 6:30 AM Monday, Wednesday, Friday
```
The operators in detail
`*` — any value
` *` means "every minute of every hour of every day." 525,600 fires per year (or 527,040 in a leap year).
`0 ` means "the 0th minute (top of hour) of any hour, any day." Fires hourly.
`,` — list
`0 9,12,18 *` means "9 AM, 12 PM, and 6 PM every day."
Lists can be in any field: `/15 9,12,18 * 1-5` means "every 15 minutes during 9, 12, and 18 hours, on weekdays."
`-` — range
`0 9-17 ` means "every hour from 9 AM to 5 PM." Equivalent to `0 9,10,11,12,13,14,15,16,17 `.
`/` — step
`/5 *` means "every 5 minutes" — minute 0, 5, 10, 15, 20, ..., 55.
`/2 *` means "every 2 minutes" — minute 0, 2, 4, 6, ..., 58.
`5/10 ` means "starting at minute 5, every 10 minutes" — minute 5, 15, 25, 35, 45, 55. (Note: only some cron implementations support starting offset.)
`?` — wildcard placeholder
In some cron variants (Spring, Quartz), `?` is used in day-of-month and day-of-week fields when the other field is restricting. Standard Unix cron uses `*` for both.
`L` — last
In Quartz cron (not standard Unix), `L` means "last." `0 0 L ` means "midnight on the last day of the month." Standard Unix cron requires more verbose patterns.
Common patterns
Daily
```
0 9 * 9 AM every day
0 0 * Midnight every day
30 22 * 10:30 PM every day
```
Weekly
```
0 9 1 9 AM every Monday
0 0 5 Midnight every Friday
/15 9-17 * 1-5 Every 15 min during 9-5 weekdays
```
Monthly
```
0 0 1 Midnight on the 1st of every month
0 12 15 Noon on the 15th of every month
59 23 1-7 * 0 11:59 PM Sunday in the first 7 days of the month
(i.e., the first Sunday of the month)
```
Hourly variations
```
0 Top of every hour
30 Half past every hour
/5 * Every 5 minutes
/30 * Every half hour (i.e., 0 and 30 every hour)
0 /2 Top of every 2nd hour (0, 2, 4, ...)
```
The day-of-week and day-of-month interaction
This is the trickiest part of cron. When both day-of-month (3rd field) and day-of-week (5th field) are restricted (not `*`), what happens?
Standard Unix cron: OR. The job runs if EITHER condition matches.
```
0 9 1 * 1
```
This means: 9 AM on the 1st of any month, OR 9 AM on Mondays. NOT "9 AM on the 1st when it's also a Monday."
Some other cron variants (Spring, Quartz): AND. The job runs only if BOTH conditions match. Use `?` in one field to disambiguate.
This OR/AND inconsistency is the source of many cron bugs. Test before deploying.
Special strings (vixie-cron extensions)
Most modern cron implementations support these convenience strings:
```
@yearly → 0 0 1 1 * Midnight, January 1
@monthly → 0 0 1 Midnight on the 1st of each month
@weekly → 0 0 0 Midnight every Sunday
@daily → 0 0 * Midnight every day
@hourly → 0 Top of every hour
@reboot Run once at startup (no time anchor)
```
Use these for clarity. `@daily` is more readable than `0 0 *`.
Time zone considerations
By default, cron runs in the system's local time zone. This creates problems:
- DST shifts can cause jobs scheduled for 2:30 AM to skip (spring forward) or run twice (fall back)
- Cross-region servers may have different time zones
- Specific time zone needs require explicit handling
Recommendations:
Run cron in UTC. Set the system clock to UTC; convert in your application logic if user-facing times matter. This avoids DST issues.
Or, use modern alternatives like systemd timers or schedulers in your application framework, which handle time zones explicitly.
Document the time zone in your cron file comments.
Common pitfalls
Day-of-week numbering: standard Unix cron uses 0-7, where 0 and 7 both = Sunday. Some others use 1-7 starting Monday.
Month numbering: 1-12, January = 1.
Day-of-month bounds: 1-31. `0 0 30 2 *` (February 30) is silently invalid.
Leap years and Feb 29: `0 0 29 2 *` runs only on Feb 29 of leap years. The job runs every 4 years.
Job running too long: if a cron job takes 2 hours but runs hourly, you get overlapping jobs. Use a lock file or single-instance constraint.
Environment variables: cron typically runs with minimal environment. PATH may not include common directories. Set explicit paths in your scripts.
Examples for SaaS-style usage
If you're using cron expressions in a SaaS product (e.g., scheduled reports, recurring webhooks):
Daily report at 8 AM in user's timezone:
```
0 8 *
```
Plus your application logic must convert to user's zone.
Run on the first business day of the month:
```
0 9 1-3 * 1-5
```
Sort of. This runs on day 1, 2, OR 3 if it's a weekday. Stop at 9 AM after the first match.
Run every 6 hours starting at midnight:
```
0 /6
```
This runs at 0, 6, 12, 18.
Run every hour during business hours (9-5):
```
0 9-17 1-5
```
Visual cron expression tools
Several tools help build and validate:
- The cron builder lets you click through a visual UI
- crontab.guru (online) explains expressions in plain English
- crontab.io (online) similar
- Most code editors have cron syntax highlighters
FAQ
What does `0 0 0` mean?
Midnight every Sunday. (Day-of-week 0 = Sunday in Unix cron.)
Can I run a cron job every 2 hours?
Yes: `0 /2 ` runs at 0, 2, 4, ..., 22. Or `/2 *` runs every 2 minutes (different).
What does `*/15` mean exactly?
In the minute field, `*/15` runs every 15 minutes — minute 0, 15, 30, 45.
Can I run a job on the last Friday of the month?
Standard Unix cron: hard to express. Easiest: run on day 25-31 if it's a Friday: `0 0 25-31 * 5`. Most platforms have extensions for "last X" patterns.
What about seconds?
Standard Unix cron is minute-precision. Some implementations (Spring, Quartz) add a 6th field for seconds.
Why does my cron job run twice on DST fall-back?
Because the duplicate hour (1:00–1:59 happens twice) means the scheduled time fires twice. Modern schedulers track UTC under the hood to avoid this; older Unix cron doesn't.
Bottom line
Cron has 5 fields (minute, hour, day-of-month, month, day-of-week) and a small set of operators (`*`, `,`, `-`, `/`). Once you understand the syntax, you can express any periodic schedule. Watch out for the day-of-month/day-of-week OR vs AND semantics, and DST edge cases at 2 AM.
For visual cron building and validation, the cron builder does the work for you.