cronuru
Dialect

Kubernetes CronJob

Kubernetes CronJob uses standard 5-field Unix cron in `spec.schedule`. With no `spec.timeZone` set, schedules run in the kube-controller-manager's own timezone — which is UTC on most managed clusters (GKE, EKS, AKS) but isn't guaranteed. The real complexity isn't the cron syntax — it's the production knobs: `concurrencyPolicy`, `startingDeadlineSeconds`, `successfulJobsHistoryLimit`, and what happens when the controller falls behind.

Quick reference

Fields
minute hour day-of-month month day-of-week
Minute range
0–59
Hour range
0–23
Day-of-month range
1–31
Month range
1–12 or JAN–DEC
Day-of-week range
0–6 (Sun=0) or SUN–SAT
Seconds
Not supported
Year
Not supported
Default timezone
kube-controller-manager's local timezone (UTC on most managed clusters). Set `spec.timeZone` to pin a zone — stable since K8s 1.27
Shortcuts
@hourly, @daily, @weekly, @monthly, @yearly
Minimum interval
1 minute

Examples

Expression Description
*/5 * * * * Every 5 minutes
0 * * * * Every hour (or use @hourly)
0 0 * * * Daily at midnight UTC (or use @daily)
0 9 * * 1-5 Weekdays at 9 AM UTC
0 0 1 * * First day of every month at midnight
@hourly Equivalent to `0 * * * *`
@daily Equivalent to `0 0 * * *`

Gotchas

  • With no `spec.timeZone`, schedules use the **kube-controller-manager's** timezone, not yours and not necessarily UTC. It's UTC on most managed clusters (GKE/EKS/AKS), but a self-managed control plane could be anything. Set `spec.timeZone: "America/Los_Angeles"` to pin a zone explicitly — stable since Kubernetes 1.27.
  • `concurrencyPolicy` defaults to `Allow` — overlapping runs spawn parallel pods. Use `Forbid` to skip if previous is still running, or `Replace` to kill and restart.
  • `startingDeadlineSeconds` controls how long the controller will try to start a missed run. Default is unset (try forever). If the controller is down for hours, you may get a flood of catch-up runs when it recovers.
  • `successfulJobsHistoryLimit` defaults to 3 — older completed Jobs are deleted automatically. `failedJobsHistoryLimit` defaults to 1. Increase if you need a longer audit trail.
  • The CronJob controller schedules new Jobs but doesn't manage their pods directly. A Job's `template.spec` controls pod-level settings like `restartPolicy: OnFailure` (recommended).

Used by

  • Kubernetes CronJob (`batch/v1` CronJob resource)
  • OpenShift CronJob (same API)
  • Knative scheduled functions (when configured for cron triggers)
  • Argo Workflows CronWorkflow

Kubernetes CronJob is mostly a thin wrapper around standard Unix cron — the schedule field looks identical to crontab. What makes K8s CronJob different in practice is everything around the schedule: concurrency policies, history limits, the controller’s catch-up behavior, and the fact that “now” is the controller-manager’s clock (usually UTC) unless you set spec.timeZone.

References

Frequently asked questions

What cron syntax does Kubernetes CronJob use?
Standard 5-field Unix cron: minute, hour, day-of-month, month, day-of-week. The `*`, `,`, `-`, `/` operators all work as expected. The `@hourly`, `@daily`, `@weekly`, `@monthly`, and `@yearly` shortcuts are also accepted, and a `?` is treated the same as `*`.
What timezone does Kubernetes CronJob use?
With no `spec.timeZone` set, the CronJob runs in the kube-controller-manager's own local timezone — which is UTC on most managed clusters (GKE, EKS, AKS) but isn't guaranteed on self-managed control planes. To pin a zone, set `spec.timeZone` to an IANA name like `America/New_York` or `Europe/London`; the field is stable as of Kubernetes 1.27. On older clusters, keep everything in UTC or offset your cron expressions manually.
How do I prevent overlapping CronJob runs?
Set `spec.concurrencyPolicy: Forbid` — Kubernetes will skip the next scheduled run if the previous Job is still active. `Replace` kills the running Job and starts a new one. `Allow` (the default) lets them run in parallel.
Why does my CronJob run multiple times all at once?
Most likely the CronJob controller was down or behind, then tried to catch up on missed runs. Set `startingDeadlineSeconds` to a short value (e.g., 60) to cap how far back the controller looks. With no deadline set, it may try to catch up on hours of missed schedules at once.
Can I run a CronJob every 30 seconds?
No — Kubernetes CronJob has a minimum granularity of 1 minute, matching standard Unix cron. For sub-minute scheduling, use a long-running Deployment with an in-process scheduler, an Argo Workflow with a loop, or a queue-based system like Celery Beat.