If you have ever typed */5 * * * * into a crontab file and hoped for the best, you already know the
problem with cron: the syntax is five tiny fields, and every one of them has a rule that is easy to get wrong. A
schedule that fires at the wrong hour, or silently skips the last day of the month, or runs on a server in a
timezone you did not pick, is more common than a schedule that is right.
A cron expression is five time fields — minute, hour, day-of-month, month, day-of-week — read left to right. Each field accepts a number, a list (8,20), a range (1-5), a step (*/15) or a wildcard (*). The server fires the job at every moment that matches all five fields.
This post is the cheat sheet for the ten schedules people actually need, followed by the handful of quirks that cause real incidents. You can test every example below in our Cron Explainer, which runs in your browser and shows the next run times without sending anything to a server.
The five fields, in one table
| Field | Range | Special values | Meaning |
|---|---|---|---|
| Minute | 0-59 | * , - / | Minute of the hour |
| Hour | 0-23 | * , - / | Hour of the day, server local time |
| Day of month | 1-31 | * , - / L | Calendar day |
| Month | 1-12 (jan-dec) | * , - / | Month of the year |
| Day of week | 0-6 (sun-sat) | * , - / | 0 and 7 are both Sunday |
Read the expression as a filter: the job runs when all five fields match the current time. That “and” sounds obvious, but it is exactly where the most common surprise comes from — more on the day-fields quirk below.
The ten schedules you actually need
| You want | Expression | Notes |
|---|---|---|
| Every 5 minutes | */5 * * * * | Steps start from 0, so runs at :00, :05, :10… |
| Every hour, on the hour | 0 * * * * | Not * * * * * — that is every minute |
| Daily at 9:00 | 0 9 * * * | Server timezone, not yours (see below) |
| Weekdays at 9:00 | 0 9 * * 1-5 | 1 = Monday |
| Twice a day, 8:00 and 20:00 | 0 8,20 * * * | Lists with a comma |
| Every Monday at 8:30 | 30 8 * * 1 | Minute first, always |
| First day of the month, midnight | 0 0 1 * * | Day of month is the third field |
| Quarterly (Jan/Apr/Jul/Oct 1st) | 0 0 1 1,4,7,10 * | Months as a list |
| Every year on January 1st | 0 0 1 1 * | The classic “happy new year” job |
| Every 10 minutes, weekdays only | */10 * * * 1-5 | Step plus range combine freely |
One near-miss worth flagging: * * * * * is “every minute”, and it is the most expensive mistake to
paste by accident. If you see a server mysteriously busy, a runaway every-minute job is a good first suspect.
The day-fields quirk that bites everyone once
Cron has two day fields: day-of-month and day-of-week. The rule in classic (Vixie) cron is:
If both day fields are restricted (neither is *), the job runs when
either matches — not both.
So 0 9 13 * 5 does not mean “Friday the 13th”. It means “every Friday, plus the 13th of
every month, whatever weekday it falls on”. Most people expect an intersection and get a union. If you actually
want Friday the 13th, classic cron cannot express it in one line; you need a wrapper script that checks the date,
or a scheduler with richer syntax.
Our Cron Explainer follows this classic semantics and shows you the actual next run times, so a union surprise shows up as visible dates before it becomes a production surprise.
Four more quirks that cause real incidents
Timezone. Cron runs in the server's local time. A job scheduled for 9:00 on a server set to UTC
fires at 18:00 in Beijing and 4:00 in New York. Worse, some hosting platforms silently run containers in UTC. If
the hour matters, check the server's timezone first — date on the same machine is the ground truth.
Steps are anchored at zero. */15 means 0, 15, 30, 45 — it does not align to when
the server booted or when the file was saved. If you need runs at :10, :25, :40, :55, write
10-59/15.
Ranges do not wrap. A night window across midnight, like 22:00 to 02:00, cannot be written as
22-2 in classic cron. Write two lines: 0 22-23 * * * and 0 0-2 * * *.
February 29th exists. 0 0 29 2 * is a valid yearly schedule that only fires on
leap years. It is a neat party trick and a genuinely correct way to run a once-every-four-years task.
How to check a schedule before trusting it
Read the expression out loud in field order — minute, hour, day-of-month, month, day-of-week — and it usually becomes obvious where you inverted something. Then verify mechanically:
- Paste the expression into the Cron Explainer and read the next five run times. Do the dates match your intent, including the timezone you assumed?
- For anything touching a specific weekday and a specific day-of-month, remember the union rule and check whether you actually wanted “either”.
- On the server itself, run the job's command once by hand. Cron only schedules; it does not make a broken command work.
If your job processes exports or logs with timestamps inside them, the Unix Timestamp Converter is handy for translating what the log says into what your clock says — the usual first step when you suspect a timezone mismatch.
Frequently asked questions
Is cron 5 fields or 6?
Classic crontab is 5. Some systems add fields: Quartz (Java) prefixes seconds for 7 total, and systemd timers replace the syntax entirely. When a parser complains, count your fields — a 6-field expression pasted into a 5-field crontab silently shifts every value one place left, which schedules the wrong thing with no error.
Why did my job not run at all?
The usual suspects, in order: the cron daemon is not running; the crontab you edited is not the crontab the
server reads (check with crontab -l as the same user); the timezone is not what you assumed; or the
expression is valid but matches nothing, like 0 9 31 2 * — February 31st never happens.
Does cron account for daylight saving time?
It runs on server local time, so a 2:30 schedule can fire twice or not at all on DST transition nights. If your server observes DST and the job matters, schedule it in UTC instead, where the clock never jumps.
What is the difference between */10 and 0,10,20,30,40,50?
Nothing, in classic cron — the step syntax is just shorthand for exactly that list. Anywhere they differ, the list is the one that does what the author meant.