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.