Cron schedule examples
58 ready-made cron expressions, each with a field breakdown, the next scheduled runs and equivalents for GitHub Actions, Kubernetes and AWS EventBridge. Need something else? Build a custom expression.
| Schedule | Expression |
|---|---|
| Every minute | * * * * * |
| Every 2 minutes | */2 * * * * |
| Every 3 minutes | */3 * * * * |
| Every 4 minutes | */4 * * * * |
| Every 5 minutes | */5 * * * * |
| Every 6 minutes | */6 * * * * |
| Every 10 minutes | */10 * * * * |
| Every 12 minutes | */12 * * * * |
| Every 15 minutes | */15 * * * * |
| Every 20 minutes | */20 * * * * |
| Every 30 minutes | */30 * * * * |
| Every hour | 0 * * * * |
| Every 2 hours | 0 */2 * * * |
| Every 3 hours | 0 */3 * * * |
| Every 4 hours | 0 */4 * * * |
| Every 6 hours | 0 */6 * * * |
| Every 8 hours | 0 */8 * * * |
| Every 12 hours | 0 */12 * * * |
| Every day at 00:00 | 0 0 * * * |
| Every day at 01:00 | 0 1 * * * |
| Every day at 02:00 | 0 2 * * * |
| Every day at 03:00 | 0 3 * * * |
| Every day at 04:00 | 0 4 * * * |
| Every day at 05:00 | 0 5 * * * |
| Every day at 06:00 | 0 6 * * * |
| Every day at 07:00 | 0 7 * * * |
| Every day at 08:00 | 0 8 * * * |
| Every day at 09:00 | 0 9 * * * |
| Every day at 10:00 | 0 10 * * * |
| Every day at 11:00 | 0 11 * * * |
| Every day at 12:00 | 0 12 * * * |
| Every day at 13:00 | 0 13 * * * |
| Every day at 14:00 | 0 14 * * * |
| Every day at 15:00 | 0 15 * * * |
| Every day at 16:00 | 0 16 * * * |
| Every day at 17:00 | 0 17 * * * |
| Every day at 18:00 | 0 18 * * * |
| Every day at 19:00 | 0 19 * * * |
| Every day at 20:00 | 0 20 * * * |
| Every day at 21:00 | 0 21 * * * |
| Every day at 22:00 | 0 22 * * * |
| Every day at 23:00 | 0 23 * * * |
| Every weekday at 09:00 | 0 9 * * 1-5 |
| Every weekend at 09:00 | 0 9 * * 6,0 |
| Every Sunday at midnight | 0 0 * * 0 |
| Every Monday at midnight | 0 0 * * 1 |
| Every Tuesday at midnight | 0 0 * * 2 |
| Every Wednesday at midnight | 0 0 * * 3 |
| Every Thursday at midnight | 0 0 * * 4 |
| Every Friday at midnight | 0 0 * * 5 |
| Every Saturday at midnight | 0 0 * * 6 |
| On the first day of every month | 0 0 1 * * |
| Every month | 0 0 1 * * |
| Every quarter | 0 0 1 1,4,7,10 * |
| Every year | 0 0 1 1 * |
| On the 15th of every month | 0 0 15 * * |
| Twice a day | 0 0,12 * * * |
| Every hour during business hours | 0 9-17 * * 1-5 |
The field layout
┌───────── minute (0 - 59)
│ ┌─────── hour (0 - 23)
│ │ ┌───── day of month (1 - 31)
│ │ │ ┌─── month (1 - 12)
│ │ │ │ ┌─ day of week (0 - 6, Sunday = 0)
│ │ │ │ │
* * * * *
Shorthand aliases
@yearly / @annually | 0 0 1 1 * |
@monthly | 0 0 1 * * |
@weekly | 0 0 * * 0 |
@daily / @midnight | 0 0 * * * |
@hourly | 0 * * * * |
@reboot | once at startup |
Day-of-month and day-of-week are OR, not AND
This is the one that catches everybody. When both the day-of-month and the day-of-week fields are restricted — neither is * — cron runs the job when either matches, not when both do.
0 0 1 * 1 # not "the 1st, if it is a Monday"
# it is "the 1st" OR "every Monday"
So that expression fires on the first of every month and on all four or five Mondays as well — roughly six times a month rather than the once you were picturing. It is specified behaviour, not a bug: POSIX says so and Vixie cron, the one behind most Linux systems, implements it.
When only one of the two is restricted the surprise disappears, which is why it stays hidden until the day you need "the first Monday of the month" — an expression standard cron cannot write at all. The usual answer is 0 0 1-7 * 1, which fires on every Monday and on the 1st to 7th, and then a guard inside the job checks the date.
What the step syntax actually counts from
*/5 in the minute field means minutes 0, 5, 10 and so on — it steps through the field's own range, not from whenever the job was installed. A job set to */5 at 09:03 first runs at 09:05, not 09:08.
Steps also apply to a range: 0-30/10 is minutes 0, 10, 20 and 30. And a step that does not divide the range evenly simply stops early rather than wrapping — */7 in the hour field gives 0, 7, 14 and 21, then a five-hour gap over midnight.
Cron has no timezone of its own
Jobs run in the system's local time, which means a schedule crossing a daylight-saving boundary either runs twice or not at all. Vixie cron treats jobs in the skipped hour as due immediately when the clock jumps forward, and jobs in the repeated hour as already run; other implementations differ, and container schedulers usually run UTC regardless of the host.
The reliable fix is to set the timezone explicitly where the scheduler allows it — CRON_TZ=UTC at the top of a crontab, timeZone in a Kubernetes CronJob, or the schedule expression's own timezone field in EventBridge — and to keep anything that must not run twice out of 01:00–03:00 entirely.
Five fields, unless it is six
Standard Unix cron takes five fields, starting at minutes. Quartz and Spring's scheduler take six, starting at seconds, so an expression copied between them is shifted by one place and silently means something else. 0 0 12 * * ? is noon in Quartz. Paste it into a crontab and Unix cron reads the first five fields as the schedule and everything after them as the command — so it becomes a job that runs at 00:00 on the 12th of every month and tries to execute a program called ?. It is accepted, it is scheduled, and it fails somewhere you are not looking, which is worse than a syntax error would have been.
Quartz also adds characters standard cron does not have — L for last, W for nearest weekday, # for "the nth weekday of the month". 0 0 12 ? * 2#1 is the first Monday of the month, which is the expression standard cron cannot express. If a snippet contains any of those, it is not a Unix crontab line.