Cron syntax explained, field by field
A practical guide to reading and writing cron expressions — the five fields, the operators, common recipes, and the timezone trap.
Cron expressions look cryptic, but they follow a small, learnable grammar. Once
you can read the five fields and four operators, 30 4 1,15 * * stops being a
puzzle. This is a practical guide — and there’s a
free explainer tool if you’d rather just paste
an expression and see what it does.
The five fields
A standard cron expression is five space-separated fields:
┌───────────── minute (0 - 59)
│ ┌─────────── hour (0 - 23)
│ │ ┌───────── day of month (1 - 31)
│ │ │ ┌─────── month (1 - 12)
│ │ │ │ ┌───── day of week (0 - 6, Sunday = 0)
│ │ │ │ │
* * * * *
Read left to right: minute, hour, day-of-month, month, day-of-week.
The four operators
*— every value.* * * * *means every minute.,— a list.1,15in day-of-month means the 1st and the 15th.-— a range.9-17in the hour field means 09:00 through 17:00./— a step.*/15in the minute field means every 15 minutes.
You combine them freely: 0 9-17 * * 1-5 is “on the hour, 9am–5pm, weekdays.”
Recipes you’ll actually use
| Expression | Meaning |
|---|---|
*/15 * * * * | Every 15 minutes |
0 9 * * 1-5 | Weekdays at 09:00 |
30 4 1,15 * * | 04:30 on the 1st and 15th |
0 */4 * * * | Every 4 hours |
0 2 * * * | Daily at 02:00 |
0 0 * * 0 | Sundays at midnight |
The day-of-month / day-of-week gotcha
If you set both day-of-month and day-of-week (to non-* values), most cron
implementations treat it as an OR, not an AND. 0 0 13 * 5 doesn’t mean
“Friday the 13th” — it means “the 13th of any month, and every Friday.” Keep one
of the two as * unless you really mean the union.
The timezone trap
The biggest source of cron surprises isn’t syntax — it’s time. Server cron usually runs in UTC, but you think in local time, and twice a year daylight saving shifts the wall clock. A job set for “09:00” in a UTC crontab drifts an hour every spring and autumn relative to your office.
The fix is to schedule in a real timezone and let the scheduler handle DST.
SteadyCron gives every job its own IANA timezone (like Europe/Berlin) and keeps
“09:00 every weekday” at 09:00 local, all year.
Try it
Paste any expression into the cron expression explainer to see it in plain English and preview the next runs in your timezone — then schedule it on SteadyCron with retries and alerts.