CronJobs left on concurrencyPolicy Allow, where slow runs can overlap
What does ZopNight detect here?
ZopNight flags any Kubernetes CronJob on EKS, GKE or AKS whose `concurrencyPolicy` is `Allow`. `Allow` is the Kubernetes default, so every CronJob that never chose `Forbid` or `Replace` qualifies: when one run outlasts its schedule interval, the next Job starts beside it and the overlapping pods compete for data and node capacity.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1750 · RC-1850 · RC-1950 |
| Category | governance |
| Severity | low |
| Metric | spec.concurrencyPolicy |
| Threshold | equals Allow |
| Source | ZopNight |
| Permissions used | list cronjobs.batch |
Where it applies
What Allow does when a run is slow
A CronJob creates a Job each time its schedule fires. The
CronJob concepts page
lists three concurrency policies and marks Allow as the default: concurrently running Jobs
are permitted. Forbid skips the new run if the previous one is still going, and Replace
cancels the running Job and starts the new one in its place.
Under Allow, a job scheduled every five minutes that suddenly takes twelve leaves two or three
copies running at once. If the job writes to a database, processes a queue or rotates files,
the copies race each other. If it is heavy, each copy requests its own CPU and memory, so a
backlog of overlapping runs can crowd other pods off the nodes. The same page also warns that
scheduling is approximate and that jobs should be idempotent, because Kubernetes can create two
Jobs for one slot in rare cases.
Finding CronJobs that allow overlap
Kubernetes fills in the default when the object is stored, so the field is almost always
present. This lists every CronJob still set to Allow, with its schedule:
kubectl get cronjobs -A -o json | jq -r ' .items[] | select((.spec.concurrencyPolicy // "Allow") == "Allow") | "\(.metadata.namespace)/\(.metadata.name) schedule=\(.spec.schedule)"'Compare that list with job durations from kubectl get jobs -A to find the ones at real risk.
One field decides the finding
The check reads concurrencyPolicy as ZopNight collected it and fires only when the value is
exactly Allow. There is no threshold on job duration and no history window: ZopNight does not
wait to see an overlap happen before flagging the setting. When the policy is not reported at
all, the CronJob is skipped.
Cases it does not try to judge
The rule cannot tell whether overlap is harmless for a given job. A job that always finishes in seconds on an hourly schedule will be flagged even though it will never overlap in practice, and some batch designs rely on parallel runs on purpose. Suspended CronJobs are not excluded; they are also reported by Suspended CronJob.
Governance finding, no dollar figure
This is a low-severity governance recommendation with no savings estimate. The cost it guards against is indirect: duplicated work, corrupted output and the extra node capacity that stacked runs consume.
Choosing Forbid or Replace
- Decide which failure mode is acceptable.
Forbidkeeps the running job and skips the new slot;Replacekills the running job so the newest run wins. - Patch the CronJob, for example
kubectl patch cronjob nightly-report -p '{"spec":{"concurrencyPolicy":"Forbid"}}'. - With
Forbid, setstartingDeadlineSecondsdeliberately. Kubernetes counts skipped slots as missed, and when it finds more than 100 missed schedules it does not start the catch-up Job and logstoo many missed start times. A deadline limits how far back that count looks. - Add
activeDeadlineSecondsto the Job template so a hung run cannot hold the slot forever.