Cron Atlas

Kubernetes CronJob schedules

Five fields in .spec.schedule, parsed by the Go library robfig/cron. It looks like Unix cron and mostly is. The differences live at the edges.

minute0
hour3
day of monthevery day
monthevery month
day of weekMon

At 03:00, on Mondays.

Next 10 runs

Clock changes

In the other dialects

“Same schedule” means the same clock times. Schedulers still treat a clock change differently, as the table on the home page shows.

Keyboard: / edit the expression, 1 to 6 change dialect, t time zone, n next clock change, c copy the link, Esc leave a field.

Fields

FieldValuesSpecial characters
Minute0–59* , - / ?
Hour0–23* , - / ?
Day of month1–31* , - / ?
Month1–12 or jan–dec* , - / ?
Day of week0–6, Sunday to Saturday, or sun–sat* , - / ?

Special characters

?
The same as *.
/
Steps over a range or star: 0-23/2, */2. The parser also accepts 5/15, meaning 5 through the top of the field.
@hourly
Macros: @yearly, @annually, @monthly, @weekly, @daily, @midnight, @hourly. @every 1h30m is accepted by the library but not listed in the Kubernetes docs.

The two day fields

If either day field is a star, both must match, which in practice means only the other one counts. If both are restricted, either can match. A stepped star such as */2 counts as restricted when the step is above 1, unlike Vixie cron.

Time zones

With no .spec.timeZone, the kube-controller-manager’s local zone. Set .spec.timeZone to an IANA name such as Etc/UTC; this has been stable since v1.27.

Clock changes

The Kubernetes docs do not cover this, so the behaviour comes from robfig/cron, whose docs warn that jobs in a leap-ahead transition do not run. Its tests confirm the rest: a time in the repeated hour runs twice. This tool ports the library’s Next loop and compares it with the real thing, which also reproduces a quirk: in Australia/Lord_Howe, whose clocks move by 30 minutes, a daily midnight job loses the midnight after the fall-back.

Gotchas

  • No 7 for Sunday. The range stops at 6.
  • Backwards ranges such as 5-1 are rejected.
  • CRON_TZ and TZ inside .spec.schedule fail validation. Use .spec.timeZone.
  • A Job is created roughly once per schedule. Kubernetes says there can be two or none, so Jobs should be idempotent.
  • If more than 100 schedules were missed since the last run, the CronJob does not start the Job and logs too many missed start times. Setting .spec.startingDeadlineSeconds changes the window that is counted.
  • A startingDeadlineSeconds under 10 can mean the CronJob is never scheduled, because the controller only checks every 10 seconds.
  • concurrencyPolicy: Forbid skips a run while the previous one is still going. Skipped runs count as missed.
  • The library gives up if nothing matches in five years, so 0 0 31 2 * has no next run at all.

Examples to try

Sources

Other dialects