Toolivaro

Reading a cron schedule: the five fields that run every server

Backups at 3 AM, reports on Mondays, health checks every quarter hour — all of them are five fields of text: minute, hour, day of month, month, day of week. Here is how to read and write cron expressions, the two traps that make them fire at the wrong time, and why the next-run check is the one that catches both.

Every server that has ever run a 3 AM backup is running a cron schedule — five fields of text that decide the cadence of the world’s automated work. They are small, cryptic, and capable of quiet betrayal: the same expression that runs a backup every night can run a report four times a month by accident, or never run it at all, and the only way to catch either failure is to know what the five fields actually mean. This page is the reading manual.

The five fields

A cron expression is five fields, separated by spaces, in exactly this order:

minute, hour, day-of-month, month, day-of-week

The classic example is 0 3 * * * — read it as: minute 0, hour 3, any day of month, any month, any day of week. A job that runs at 3:00 AM every day. The bounds are strict and worth memorizing because they catch most hand-typed mistakes: minutes 0–59, hours 0–23, days of month 1–31, months 1–12, days of week 0–7 — with the quirk that both 0 and 7 mean Sunday, exactly as the man page says, and 1–6 are Monday through Saturday.

Each field accepts the same set of notations:

  • A single value3 means exactly 3.
  • A wildcard* means every value in range.
  • A range9-17 means every hour from 9 through 17.
  • A step*/15 means every 15th value starting from the first; 0-30/5 means every 5 minutes within the first half hour.
  • A comma list0,30 means exactly those two values.
  • Names — months and weekdays can be spelled as cron itself spells them: jan-dec, sun-sat, and even ranges like mon-fri and jan-mar. (Names are accepted in the month and weekday fields only; there is no name for minute 30.)

The notation composes: 0 9-17 * * 1-5 is “on the hour, 9 AM through 5 PM, Monday through Friday” — the classic business-hours schedule.

Reading a few real schedules

ExpressionPlain meaning
0 3 * * *3:00 AM, every day
*/15 * * * *Every 15 minutes, around the clock
0 9-17 * * 1-5Hourly, 9 AM–5 PM, Mon–Fri
0 0 * * 0Midnight, every Sunday
0 0 1 * *Midnight, on the 1st of every month
30 2 * * 12:30 AM, every Monday

Each reads the same way: fill in the fields from left to right and say what each means. 30 2 * * 1 — “minute 30, hour 2, any day, any month, Monday” — is a Monday-morning maintenance window. Nothing about the syntax requires reading right to left; the fields are positional, and the order never changes.

Trap one: the OR rule

The most famous cron surprise hides in the last two fields. In most scheduling systems, “the 1st of the month” and “Monday” would mean both conditions must hold. In cron, when both day-of-month and day-of-week are restricted, the job fires when either matches. The expression 0 0 1 * 1 — intended as “midnight on the 1st, and also on Mondays” — actually means “midnight on the 1st, or midnight on any Monday”, which is roughly four times as often as its author expected. The rule is documented in every real cron man page, yet it is the most common source of “why is my job running every week?” confusion in production — which is why a builder that writes the plain-language description for you is not a luxury; it is the difference between the schedule you wrote and the schedule the daemon will run.

Trap two: time zones

A cron daemon runs in the time zone of the machine it lives on — the server’s, or the container’s, never “the user’s”. 0 3 * * * means 3 AM there, and if the server is in UTC and you are in Warsaw, your “3 AM backup” happens at 5 AM your time in summer and 4 AM in winter. The same job on a fresh container may silently move by an hour when the container’s clock defaults to UTC. The rule that keeps this from being a surprise: when you schedule a job, the time zone in the expression is the machine’s, and the description should say which one it is. (The subtler cousin of this trap is daylight saving — a job pinned to a local time that the zone skips on a spring-forward day does not get a second chance at that hour; the daemon simply does not run it.)

The next-run check: the one verification that catches both

Both traps are invisible in the expression itself — the text 0 0 1 * 1 looks nothing like “four times a month”, and 0 3 * * * says nothing about zones. The check that exposes them is the next-run computation: derive the actual next instant the daemon will fire, by evaluating the real matching rules minute by minute. A schedule with the OR rule shows its extra runs in the next few results; an impossible date like 0 0 31 2 * (31 February) produces no next run at all — and the honest answer is “no next run”, not a fabricated time. This is the difference between a builder and a generator: a generator converts your picks into an expression and stops, which means it cannot tell you that your expression is a lie; a builder computes the schedule the daemon will actually keep and shows it to you before the job goes anywhere near production.

Let the calculator do it

The cron builder assembles five-field expressions with the full notation — wildcards, ranges, steps, comma lists, and month/weekday names — validating each field against its real bounds and rejecting invalid input with a message that says what is wrong instead of silently “fixing” it. From the parsed specification it derives the canonical expression, a plain-language description, and the next real run times, computed minute-by-minute against the daemon’s actual matching rules — with the OR rule honored and shown in the description, the time zone stated (your browser’s local time, because that is what the page can know), and impossible dates reported honestly. Everything runs locally, and nothing you type is uploaded or stored.

The one rule

Never deploy a cron expression you have not seen the next-run times for. The five fields are a contract with a daemon that will keep its promises literally — including the ones you did not intend. Read the fields left to right, know the OR rule, name the time zone, and let the next-run computation be the proof that the schedule does what you think it does.

Frequently asked questions

What do the five fields in a cron expression mean?

Minute (0–59), hour (0–23), day of month (1–31), month (1–12), and day of week (0–7, where both 0 and 7 mean Sunday). Each field can hold a single value, a range like 9-17, a step like */15, a comma-separated list, or a wildcard. The expression 0 3 * * * means “at minute 0 of hour 3, every day”.

Why does 0 0 1 * 1 run far more often than expected?

Because of cron's OR rule: when both day-of-month and day-of-week are restricted, the job fires when EITHER matches — so this expression means “midnight on the 1st, or midnight on any Monday”, roughly four times a month instead of once. It is the most documented surprise in cron syntax, and any builder that does not show it is hiding it.

Which time zone does cron use?

The daemon's local time — the server's, or the container's, not yours. A job written as 0 3 * * * fires at 3 AM in whatever time zone the machine runs in. Tools that compute next-run times in your browser's zone must say so, and the honest ones do.

How do I run a job every 15 minutes?

The step notation: */15 in the minute field — every 15 minutes, around the clock. Steps work with ranges too (0-30/5 means every 5 minutes in the first half hour), and a comma list gives exact picks: 0,30 means top and bottom of the hour.

What happens if a schedule can never fire, like 31 February?

The honest answer is that it does not fire — and an honest tool reports “no next run within the horizon” instead of inventing one. A daemon silently skips such jobs forever; a builder that shows the problem before the job reaches production is doing you the favor.

What does 7 mean in the day-of-week field?

Sunday — the classic man page permits 0–7, where 0 and 7 both mean Sunday. Using 7 for Sunday is legal but rare; most schedules use 0, and 1–6 are Monday through Saturday.

Last reviewed August 25, 2026 · Version 1.0.0 · Toolivaro does not guarantee external content.

Found a mistake or have a correction? Report it — we review every correction.