Cron expression examples
The schedules people actually need, grouped by how often they run — each one described by the same parser the explainer uses, so the wording is never out of date.
Cron expression reference
Every description below comes from the parser rather than a written label. Open any expression in the explainer to see when it fires next.
Every few minutes
Step values divide the hour evenly only when the step divides 60. */7 restarts at the top of every hour, so the gap between 56 and the next run is four minutes, not seven.
Hourly
Leaving the minute as * is the single most common cron mistake: it runs sixty times an hour, not once. Pin the minute.
-
0 9-17 * * * - Hourly during business hours At 09:00, 10:00, 11:00, 12:00, 13:00, 14:00, 15:00, 16:00, and 17:00, every day.
- Open
Daily
A job scheduled between 01:00 and 03:00 can run twice or not at all on daylight-saving changeover days. Pick a time outside that window for anything that must not repeat.
-
0 0 * * 1-5 - Every weekday at midnight At 00:00, on Monday, Tuesday, Wednesday, Thursday, and Friday.
- Open
Weekly
Sunday is both 0 and 7. Both are accepted, and 1-5 is Monday to Friday — the week starts on Sunday, which trips people up when writing ranges.
-
0 9 * * 1-5 - Every weekday at 09:00 At 09:00, on Monday, Tuesday, Wednesday, Thursday, and Friday.
- Open
Monthly and yearly
Day 31 simply does not fire in months that lack it — there is no rounding. To catch the true end of a month, run daily and check the date in your own code.
-
0 4 1 * 1 - Both day fields set — an OR, not an AND At 04:00, on the 1st or on Monday (whichever matches).
- Open
The five fields, in order
Every expression above is five fields separated by spaces, and they always appear in the
same order: minute (0–59), hour (0–23),
day of month (1–31), month (1–12), and
day of week (0–6, where 0 is Sunday). An asterisk means "every value", so
the four asterisks in */5 * * * * are doing the work of saying "every hour of
every day of every month".
Within a field you can write a single number, a list (1,15), a range
(9-17), or a step (*/10, or 9-17/2 to step within a
range). That is the entire syntax. Everything else that looks complicated is a combination
of those four things.
The four mistakes that account for most of them
- Leaving the minute as an asterisk.
* 3 * * *does not run at three o'clock — it runs sixty times, once a minute, for the whole of the 03:00 hour. If a field is more specific than the one to its left, the left one almost always needs pinning. - Assuming steps divide evenly.
*/7restarts its count at the top of every hour, so it fires at :00, :07 … :56, and then again four minutes later at :00. Only steps that divide 60 — 2, 3, 4, 5, 6, 10, 12, 15, 20, 30 — produce an even rhythm across the hour boundary. - Reading the two day fields as AND. When both the day of month and the day
of week are restricted, cron ORs them.
0 4 1 * 1runs on the first of the month and on every Monday, not on first-of-the-month-if-Monday. - Forgetting the timezone. Kubernetes, GitHub Actions, and most cloud schedulers interpret schedules in UTC unless told otherwise, so a schedule written for local business hours will fire at a different local time — and will drift by an hour when daylight saving changes, because UTC does not.
Daylight saving, and why 02:30 is a bad time
On a machine running in a timezone that observes daylight saving, the hour between 01:00 and 03:00 is not a normal hour twice a year. In spring it does not exist — the clock jumps straight past it — so a job scheduled at 02:30 does not run at all. In autumn it happens twice, so the same job runs twice. Different cron implementations handle this differently, and none of them handles it in a way you would want to depend on. Schedule anything important outside that window, or run the scheduler in UTC.
To check a specific expression against real dates, paste it into the explainer, which lists the next several fire times. To build one from scratch without memorising the field order, use the builder.
Common questions
What is the cron expression for every 5 minutes?
*/5 * * * * — the */5 in the minute field means "every fifth minute starting from zero", so it fires at :00, :05, :10 and so on through :55. The four asterisks after it mean every hour, every day of the month, every month, and every day of the week. Note that this only divides the hour evenly because 5 divides 60; a step like */7 restarts its count at the top of each hour, so the gap from :56 to the next run is four minutes rather than seven.
How do I run a job once a day?
Pin both the minute and the hour: 0 3 * * * runs at 03:00 every day. The mistake to avoid is leaving the minute as an asterisk — 0 * * * * is hourly, but * 3 * * * runs sixty times, once every minute between 03:00 and 03:59. If the job must not run twice, avoid scheduling it between 01:00 and 03:00, because that window is repeated on the day the clocks go back and skipped on the day they go forward.
Why does my job with both a date and a weekday run more often than expected?
Because when the day-of-month and day-of-week fields are both restricted, cron treats them as OR rather than AND. 0 4 1 * 1 does not mean "the first of the month, if it is a Monday" — it means "the first of the month, and also every Monday". This is a genuine quirk of the format rather than an implementation detail, and it is inherited by almost every cron-compatible scheduler. To get the AND behaviour, schedule the weekday and test the date inside your own job.
Which day is 0 — Sunday or Monday?
Sunday. The week runs 0 for Sunday through 6 for Saturday, and 7 is also accepted as Sunday, so both 0 and 7 work. The practical consequence is that 1-5 is Monday to Friday, which is what you want for weekdays, while 0-4 is Sunday to Thursday and is almost never what anyone means. Weekends are 6,0 or equivalently 0,6.
What happens on the 31st in a month that has 30 days?
Nothing — the job simply does not run that month. There is no rounding down to the last day and no catch-up; the schedule matches a date that does not exist, so it never fires. The same applies to 29 February in three years out of four. If you need the actual end of the month, schedule the job daily and have it exit early unless tomorrow is the first, which is the only reliable way to express it in five fields.
Do these work in Kubernetes and GitHub Actions?
Yes for all the standard five-field expressions on this page. Kubernetes CronJob and GitHub Actions both use the same syntax, as do most cloud schedulers. Two differences worth knowing: neither supports the non-standard @reboot shortcut, and both interpret schedules in UTC by default rather than in the machine's local timezone, so a job written for 09:00 local will fire at a different local hour unless you set the timezone explicitly.