The manifest
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
timeZone: "Europe/London"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report
image: your-image:latest
timeZone is supported — unlike most platforms
Since Kubernetes 1.27 the spec.timeZone field is stable and takes any IANA name. Without it the schedule uses the kube-controller-manager's timezone, which is almost always UTC. This is one of the few schedulers that handles timezones properly, so use it.
On 1.24–1.26 the field exists behind the CronJobTimeZone feature gate. Below 1.24 it is ignored entirely — check your cluster version before relying on it.
concurrencyPolicy decides what happens when jobs overlap
Allow (the default) lets a new run start while the previous one is still going. For anything that touches shared state that is a data-corruption bug waiting to happen. Forbid skips the new run; Replace kills the old one and starts fresh. Pick deliberately rather than inheriting the default.
The 100-missed-schedules failure
If the controller can't start a job — node pressure, a paused cluster, a suspended CronJob — it counts the misses. After 100 missed schedules it stops trying entirely and logs Cannot determine if job needs to be started. The CronJob then never runs again until you recreate it.
startingDeadlineSeconds limits how late a job may start. Setting it also narrows the window the controller looks back over, which is the usual fix for the 100-miss trap.
Frequently asked questions
Does Kubernetes CronJob support timezones?
Yes. Set spec.timeZone to an IANA name such as America/New_York. Stable since Kubernetes 1.27.
Why did my Kubernetes CronJob stop running?
The most common cause is the 100-missed-schedules limit: after 100 misses the controller gives up permanently. Setting startingDeadlineSeconds prevents it.
How do I stop Kubernetes CronJobs overlapping?
Set concurrencyPolicy: Forbid. The default is Allow, which permits concurrent runs.
Related cron expressions
Browse the full cron expression reference, or build your own expression from scratch.