Guide
How to Read and Write Cron Expressions: A Beginner's Guide
Cron expressions look cryptic at first, but they follow a small, predictable set of rules. This guide walks you through reading and writing them from scratch.
If you have ever seen something like 0 9 * * 1-5 in a crontab, a CI
config, or a cloud scheduler and had no idea what it meant, you are in the right
place. By the end of this guide you will be able to read an expression like that at a
glance and write your own. You can also paste any expression into the
cron expression generator to see it explained in plain English as you
read along.
What is a cron expression?
A cron expression is a short string that tells a scheduler
when to run a task, over and over, on a fixed schedule. The name comes from
cron, the time-based job scheduler that has shipped with Unix and Linux
systems for decades.
You will run into cron expressions in a lot of places:
- Classic Unix/Linux
crontabfiles - CI/CD pipelines (for example, scheduled GitHub Actions workflows)
- Container orchestration (Kubernetes
CronJobs) - Cloud schedulers and serverless platforms
This guide covers the classic five-field format used by Vixie cron,
which is the de-facto standard. Some systems add an optional seconds field or accept
shorthand like @daily, but the five fields below are what you will see
most often.
The five fields
A standard cron expression is five values separated by spaces. Order matters — it always reads left to right as minute, hour, day of month, month, day of week:
| Position | Field | Allowed values |
|---|---|---|
| 1 | Minute | 0-59 |
| 2 | Hour | 0-23 (24-hour clock) |
| 3 | Day of month | 1-31 |
| 4 | Month | 1-12 or JAN-DEC |
| 5 | Day of week | 0-6 (0 = Sunday) or SUN-SAT |
So 30 9 * * 1 reads as: minute 30, hour 9, any day of the month, any
month, on day-of-week 1 (Monday) — in other words,
09:30 every Monday. The * means "every value", which we
cover next.
Good to know: in the day-of-week field, both 0 and
7 mean Sunday. You can also use three-letter names like MON
or JAN instead of numbers.
Not sure which field is which? Build one field by field in the generator and watch the expression update as you make choices.
Special characters: * , - /
Each field can hold more than a single number. Four characters do almost all the work:
* — every value
An asterisk matches every value of that field. * * * * * means "every
minute of every hour of every day" — the most frequent schedule cron allows.
, — a list of values
A comma lets you list several specific values. 0 9,17 * * * runs at
09:00 and 17:00 every day.
- — a range
A hyphen defines an inclusive range. 0 9-17 * * * runs at the top of
every hour from 09:00 through 17:00.
/ — steps
A slash means "every N". */15 * * * * runs every 15 minutes
(at :00, :15, :30 and :45). You can combine a step with a range, too:
0-30/10 in the minute field means minutes 0, 10, 20 and 30.
Watch out: */15 is aligned to fixed boundaries (0, 15,
30, 45), not "15 minutes after the job starts". A step always counts from the
beginning of the field's range.
Ready-to-use examples
Here are common schedules you can copy as-is:
| Expression | Meaning |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
0 * * * * | Every hour, on the hour |
0 0 * * * | Every day at midnight |
0 9 * * 1-5 | At 09:00, Monday to Friday |
30 2 * * 0 | At 02:30 every Sunday |
0 0 1 * * | At midnight on the 1st of every month |
0 */6 * * * | Every 6 hours (00:00, 06:00, 12:00, 18:00) |
Paste any of these into the generator to read them in plain English and preview the next few run times.
Common mistakes to avoid
-
Mixing up the field order. Minute comes first, not the hour.
9 0 * * *is 00:09, not 09:00 — an easy and common slip. -
The day-of-month / day-of-week trap. When both the
day-of-month and day-of-week fields are restricted (neither is
*), cron runs the job when either one matches — not both. So0 0 13 * 5runs on the 13th and on every Friday, not only on Friday the 13th. -
Expecting steps to count from "now". As above,
*/15fires at fixed minutes (0, 15, 30, 45), regardless of when the job was deployed. -
Out-of-range values. Minutes only go up to 59 and hours to 23.
There is no minute 60 or hour 24 — use
0instead. - Forgetting the timezone. Cron usually runs in the server's local time, which is often UTC. The same expression can fire at a different wall-clock time depending on where the job runs.
When in doubt, check it in the generator: it explains the expression in words and lists the next run times, so you can confirm it does what you expect.