Cron Expression Explainer

Paste a cron expression and read what it really does, with the next scheduled runs listed in your timezone.

What it means
Field by field
Next scheduled runs

The five fields, and why the order is easy to forget

PositionFieldRangeExample
1Minute0–590, */15, 30
2Hour0–239-17, */2
3Day of month1–311, */2, 15,28
4Month1–12 or JAN–DEC*, 1,4,7,10
5Day of week0–7 or SUN–SAT1-5, 0, 6,7

Some implementations accept a sixth field at the front for seconds, and 0 and 7 both mean Sunday. The order — minute first, day of week last — is the opposite of how people say things out loud, which is why "every day at 9" so often gets written as 9 * * * *, meaning "at nine minutes past every hour".

The rule that causes real bugs: day-of-month and day-of-week are OR'd

In most contexts cron fields combine with AND: minute 30 AND hour 9 AND January. Day of month and day of week are the exception. When both are restricted — neither is * — the job runs when either matches.

So 0 0 1 * MON does not mean "the first Monday of the month". It means midnight on the 1st of the month, and also midnight every Monday. If one of the two is *, normal AND behaviour applies to the other.

There is no cron syntax for "the first Monday". The usual workaround is to run every Monday and let the job itself check the date: 0 0 * * 1 combined with a guard such as [ $(date +\\%d) -le 7 ] && your-command.

Cron does not guarantee your job runs

Cron wakes up each minute and starts whatever is due. It does not know or care whether the previous run finished. A job scheduled every minute that takes ninety seconds will overlap with itself, and if the handler does not lock, you will end up with two concurrent copies and whatever corruption that causes.

Cron also does not catch up. If the machine is asleep or powered off at 03:00, that run simply does not happen — there is no queue. On a laptop or any host that can suspend, this matters; systemd timers with Persistent=true exist largely to address it.

Timezone and daylight saving

Cron runs on the system clock, in the system timezone, unless your implementation lets you set CRON_TZ. That has two consequences people hit in production:

  • A containerised job almost always runs in UTC while everyone reading the schedule assumes local time.
  • Daylight saving either skips or repeats an hour. A job at 02:30 does not run on the spring-forward day and runs twice on the fall-back day. Jobs scheduled between 01:00 and 03:00 local time are the ones that bite.

The next-run times above are computed in your browser's timezone, which is what your machine is set to — not necessarily what the server that will actually run the job is set to.

Shorthand you can use instead

ShorthandEquivalentMeaning
@hourly0 * * * *Top of every hour
@daily0 0 * * *Midnight every day
@weekly0 0 * * 0Midnight on Sunday
@monthly0 0 1 * *Midnight on the first
@yearly0 0 1 1 *Midnight on 1 January
@reboot—Once at startup

Frequently asked questions

Why does 0 0 1 * 1 run twice in some months?

Because day-of-month and day-of-week are OR'd when both are set. That expression means midnight on the 1st, plus midnight every Monday. To run only on the 1st, write 0 0 1 * *.

What does */15 actually step over?

It steps over the whole range of that field, not from "now". In the minute field */15 means minutes 0, 15, 30 and 45 — always aligned to the hour boundary. A step of */7 in the minute field gives 0, 7, 14 … 56, then wraps to 0, so the last gap is shorter than the rest.

Is 7 a valid day of week?

Yes, 7 means Sunday, the same as 0. It exists so that ranges like 6-7 can express "the weekend" — though 0,6 is the more common way to write it.

How do I run something every 90 seconds?

You cannot, with a single expression — the minute field only steps within an hour. The usual approach is a short sleep loop, or three entries offset by 30 seconds each. Quartz-style schedulers support seconds; standard Vixie cron does not.

Why are my next-run times different from the server's?

Timezone. This page computes in your browser's timezone; cron uses the host's, which for servers and containers is usually UTC. Check with `date` on the host rather than assuming.

Privacy. The expression is parsed in your browser. Nothing is sent anywhere, so it is safe to paste schedules from internal systems.