Cron syntax explained: reading the five fields without a cheat sheet
Cron expressions like 0 3 * * 1 pack a schedule into five fields. How to read them, the day-of-month vs day-of-week trap, and the mistake that fires 60 times.
By ToolsNow · Published
0 3 * * 1 runs at 3:00 every Monday. */15 * * * * runs every quarter
hour. Cron’s five-field syntax has been scheduling the world’s background
jobs since the 1970s, and it’s worth ten minutes of actually learning
rather than looking up every time. The notation is compact, and its two or
three classic traps all look right until a job fires 60 times in a row or
quietly never runs at all.
The five fields
Read left to right. Each field answers one question:
┌───────── minute (0–59)
│ ┌─────── hour (0–23)
│ │ ┌───── day of month (1–31)
│ │ │ ┌─── month (1–12)
│ │ │ │ ┌─ day of week (0–6, Sunday = 0)
│ │ │ │ │
0 3 * * 1
* means “every”. Past single values, each field also takes:
- Lists:
0,30, so on the hour and half hour. - Ranges:
9-17, nine through seventeen inclusive. - Steps:
*/15for every 15th value, or9-17/2for every 2 hours between 9 and 17.
Which gives you 30 8 1 * * for 08:30 on the first of each month, and
0 9-17 * * 1-5 for on the hour, business hours, weekdays.
Trap one: the asterisk you forgot
* 3 * * * does not run at 3am. It runs at every minute of the 3am hour,
which is sixty executions. To get a schedule that fires once an hour or
less often, the minute field has to be pinned: 0 3 * * *.
This is the most common cron mistake there is, and it’s usually discovered via sixty identical emails.
The reverse mistake exists too. 0 */1 * * * and 0 * * * * are the same
schedule, hourly on the hour. But */60 * * * * isn’t a valid way to say
it, because steps beyond a field’s range behave inconsistently from one
implementation to the next.
Trap two: day-of-month OR day-of-week
Here’s the odd one out in cron’s logic. When both day fields are restricted, standard cron runs the job when either one matches. Not both.
So 0 0 13 * 5, which reads as “the 13th, and Friday”, fires on every 13th
and every Friday. It will not wait for Friday the 13th. Every other pair
of fields combines with AND; this one is OR, for historical reasons, and it
catches everyone exactly once.
Quartz and some other modern schedulers changed the behaviour, so the same expression can schedule differently on different systems. Worth checking when you move jobs between platforms.
Trap three: whose midnight?
0 0 * * * runs at midnight somewhere. Cron evaluates times in the
system’s own time zone, so an identical crontab means different moments on
a UTC server and a local-time laptop.
Worse, a job scheduled at 02:30 system-local can be skipped or run twice on DST switch nights, because that wall-clock time doesn’t always exist. Servers pinned to UTC dodge the entire class of problem, and the price is doing the zone arithmetic yourself when what you actually meant was “3am local”.
Dialects and extensions
The five-field form is the portable core, but you’ll meet variants:
- Names.
MONandJANwork in most modern crons, so0 3 * * MONis fine. - Six or seven fields. Quartz (Java) prepends seconds and appends an optional year. A Quartz expression pasted into Unix cron is either invalid or, worse, valid and wrong.
- Macros.
@daily,@hourly,@weekly,@reboot. Readable shorthands, and@reboothas no five-field equivalent at all. - Sunday. Both 0 and 7 mean Sunday in most implementations, which is a mercy if you’re prone to off-by-one.
Since “valid cron” depends on which cron, the reliable check is translating the thing back into English. The cron expression tool parses an expression, describes the schedule in words and lists the next few runs, which catches every-minute accidents and OR-logic surprises before they reach a server.
We hit the month-index bug ourselves while building it: month numbers are
1-based, arrays are 0-based, and 0 0 1 1 * briefly described itself as
running in February. Translate, then trust.
A reading habit that prevents most errors
Read any expression aloud, right to left. “On Mondays, in any month, on any day-of-month, at hour 3, at minute 0.”
Then ask three questions. Is the minute pinned? Are both day fields restricted, which means OR? Whose time zone? Those three cover nearly everything that goes wrong between a crontab and reality.
Published by ToolsNow. Read how tools and sources are checked.
Found a mistake or an outdated detail? Send a correction with the article title and the detail to review.