Cron Cheat Sheet

Field order and ranges, every special character, the four dialects compared, and a table of the schedules people actually write.

Field order

┌───────────── minute       (0 - 59)
│ ┌─────────── hour         (0 - 23)
│ │ ┌───────── day of month (1 - 31)
│ │ │ ┌─────── month        (1 - 12, or JAN-DEC)
│ │ │ │ ┌───── day of week  (0 - 7, or SUN-SAT; 0 and 7 are both Sunday)
│ │ │ │ │
* * * * *

The order that catches people out is day of month, month, day of week — the two day fields are not adjacent.

Ranges by dialect

FieldStandardnode-cronQuartzEventBridge
Second—0–590–59—
Minute0–590–590–590–59
Hour0–230–230–230–23
Day of month1–311–311–31, L W1–31, L W
Month1–121–121–121–12
Day of week0–7 (0 = Sun)0–7 (0 = Sun)1–7 (1 = Sun), L #1–7 (1 = Sun), L #
Year——OptionalRequired
Total fields55 or 66 or 76

Special characters

CharacterMeansExampleWhere
*Every value* * * * *All
,A list0 9,12,17 * * *All
-A range0 9-17 * * *All
/A step*/15 * * * *All
?No specific value — exactly one of the two day fields must use it0 0 9 ? * 2Quartz, EventBridge
LLast day of the month0 0 23 L * ?Quartz, EventBridge
L-3Three days before the end of the month0 0 9 L-3 * ?Quartz
5LThe last given weekday of the month0 0 17 ? * 6L → last FridayQuartz, EventBridge
WNearest weekday to that date, never crossing into another month0 0 9 15W * ?Quartz, EventBridge
LWLast weekday of the month0 0 9 LW * ?Quartz, EventBridge
#The nth given weekday of the month0 0 9 ? * 2#1 → first MondayQuartz, EventBridge
@dailyShorthand for a whole expression@daily = 0 0 * * *Standard, node-cron

Shorthands

ShorthandEquivalentMeans
@yearly / @annually0 0 1 1 *Midnight, 1 January
@monthly0 0 1 * *Midnight on the 1st
@weekly0 0 * * 0Midnight on Sunday
@daily / @midnight0 0 * * *Every midnight
@hourly0 * * * *On the hour
@reboot—Once at start-up. Not a schedule, and not supported everywhere

Common schedules, in all four dialects

ScheduleStandardnode-cronQuartzEventBridge
Every minute* * * * *0 * * * * *0 * * * * ?* * * * ? *
Every 5 minutes*/5 * * * *0 */5 * * * *0 0/5 * * * ?*/5 * * * ? *
Every 15 minutes*/15 * * * *0 */15 * * * *0 0/15 * * * ?*/15 * * * ? *
Every hour, on the hour0 * * * *0 0 * * * *0 0 * * * ?0 * * * ? *
Every 6 hours0 */6 * * *0 0 */6 * * *0 0 0/6 * * ?0 */6 * * ? *
Every day at midnight0 0 * * *0 0 0 * * *0 0 0 * * ?0 0 * * ? *
Every day at 09:000 9 * * *0 0 9 * * *0 0 9 * * ?0 9 * * ? *
Weekdays at 09:000 9 * * 1-50 0 9 * * 1-50 0 9 ? * 2-60 9 ? * 2-6 *
Every Monday at 09:000 9 * * 10 0 9 * * 10 0 9 ? * 20 9 ? * 2 *
Weekends at 02:000 2 * * 0,60 0 2 * * 0,60 0 2 ? * 1,70 2 ? * 1,7 *
First day of the month at 00:000 0 1 * *0 0 0 1 * *0 0 0 1 * ?0 0 1 * ? *
Last day of the month at 23:000 23 28-31 * *0 0 23 28-31 * *0 0 23 L * ?0 23 L * ? *
First Monday of the month at 09:000 9 1-7 * 10 0 9 1-7 * 10 0 9 ? * 2#10 9 ? * 2#1 *
First day of each quarter at 00:000 0 1 1,4,7,10 *0 0 0 1 1,4,7,10 *0 0 0 1 1,4,7,10 ?0 0 1 1,4,7,10 ? *
Once a year on 1 January0 0 1 1 *0 0 0 1 1 *0 0 0 1 1 ?0 0 1 1 ? *

Two of these are approximations in the Unix dialects. Last day of the month becomes 28-31, which fires on up to four days, and first Monday relies on the OR rule and fires far more often than the name suggests. Both need a date check inside the job. The generator says so when you pick them.

The OR rule

In standard cron and node-cron, when both the day-of-month and day-of-week fields are restricted, the job runs when either matches:

0 9 1 * 1     # the 1st of the month OR every Monday — about 5 runs a month
0 9 1 * *     # the 1st of the month only
0 9 * * 1     # every Monday only

Quartz and EventBridge sidestep this by requiring ? in the field you are not using, which is why their expressions look cluttered and are less ambiguous.

Timezones

SchedulerDefault timezoneHow to change it
Unix crontabThe server’s local timeCRON_TZ=Europe/Berlin above the entry
Kubernetes CronJobUTCspec.timeZone
AWS EventBridgeUTCScheduleExpressionTimezone (Scheduler)
GitHub ActionsUTCNot configurable — adjust the expression
QuartzThe JVM defaultCronScheduleBuilder.inTimeZone(...)

Dialect notes

Standard cron

Used by: crontab, Kubernetes CronJob, GitHub Actions, most Linux schedulers

  • Five fields, with no seconds — the finest resolution is one minute.
  • Day-of-week accepts 0 to 7, where both 0 and 7 mean Sunday.
  • When day-of-month and day-of-week are both restricted, the job runs when EITHER matches — not both. This surprises almost everyone the first time.
  • Supports the @yearly, @monthly, @weekly, @daily and @hourly shorthands.

node-cron

Used by: the node-cron npm package, and several other Node schedulers

  • Six fields, with seconds first. Five fields are also accepted and are read as standard cron.
  • This leading seconds field is the usual cause of confusion: a five-field expression means something different here than the same six characters with a seconds field prepended.
  • Day-of-week numbering follows Unix — 0 to 7, with 0 and 7 both Sunday.
  • No support for L, W or # — those are Quartz extensions.

Quartz

Used by: Quartz Scheduler, Spring @Scheduled, Elastic Job, Camel

  • Six fields, with an optional seventh for the year.
  • Day-of-week is 1 to 7 with 1 meaning SUNDAY — one higher than Unix cron for every day. A Unix "1" (Monday) is a Quartz "2".
  • Exactly one of day-of-month and day-of-week must be ?. Specifying both is an error, not an OR.
  • Adds L (last), W (nearest weekday) and # (nth weekday of the month).
  • No @daily-style shorthands.

AWS EventBridge

Used by: EventBridge rules, EventBridge Scheduler, CloudWatch Events

  • Six fields, with no seconds but a required year field.
  • Written as cron(0 9 * * ? *) in the console and in CloudFormation — the wrapper is part of the syntax.
  • Day-of-week is 1 to 7 with 1 meaning Sunday, as in Quartz.
  • Exactly one of day-of-month and day-of-week must be ?.
  • Schedules are evaluated in UTC unless you set a timezone on the rule, which is the single most common cause of an EventBridge job firing an hour or more away from where it was expected.
  • The minimum interval is one minute, and delivery is best-effort rather than exact.

Frequently asked questions

What is the difference between */5 and 0/5?

Nothing, in practice. */5 means "every 5 starting from the lowest value" and 0/5 means "starting at 0, every 5" — the same set. Quartz documentation prefers 0/5 and Unix documentation prefers */5. Both are accepted by both.

Does cron guarantee my job runs exactly once?

No, and this is the assumption that causes the most damage. Schedulers retry on failure, distributed schedulers can fire on more than one node, a daylight-saving change can duplicate or skip an hour, and a machine that was asleep may run a missed job late. Make scheduled jobs idempotent — that is cheaper than making the scheduler exact.

What happens if my job takes longer than the interval?

It depends on the scheduler. Unix cron will happily start a second copy while the first is running; Kubernetes CronJobs let you choose with concurrencyPolicy; Quartz can be told the job is non-concurrent. If overlap matters, take a lock inside the job rather than relying on the schedule to prevent it.

Which field is the day of week?

The last one in standard cron (five fields), the last one before the year in Quartz and EventBridge. Miscounting it is common because the day-of-month field sits directly before the month field, so an expression reads as "day, month, weekday" rather than the "day, weekday, month" people expect.

Found a mistake on this page? Tell me — a page that is confidently wrong is worse than no page.