# What should happen when a Kubernetes CronJob is still running at its next scheduled time?

> Pick skip, overlap, or replacement from the job’s side effects and freshness requirements instead of inheriting Kubernetes defaults by accident.

Canonical URL: https://www.devobs.io/articles/qa-cronjob-overlap-policy/
By: Ines Costa
Published: 2025-09-27T10:56:47.297Z
Updated: 2026-09-06T10:18:15.722Z
Section: Architecture

If a Kubernetes CronJob is still running when the next schedule arrives, decide the outcome from the job’s business effect. Use `Forbid` when duplicate execution would be harmful, `Allow` when overlapping runs are independently safe, and `Replace` when stale work should be abandoned for fresher output. Kubernetes exposes these choices through `.spec.concurrencyPolicy`, and also lets you skip starts that are too late with `.spec.startingDeadlineSeconds` in the official [CronJob documentation](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/).

## Which CronJob policy should you choose?

The Kubernetes [CronJob documentation](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) defines three concurrency policies. `Allow` permits concurrent runs. `Forbid` skips the new run if the previous one is still active. `Replace` terminates the current run by replacing it with a new one.

That means the policy should follow the kind of mistake you can tolerate.

Choose `Forbid` for duplicate-sensitive work: invoice creation, payout files, entitlement backfills, or any task where two overlapping runs could create conflicting writes. In these cases, a skipped run is usually safer than two writers operating at once.

Choose `Allow` only when overlap is safe by design. Good examples are jobs that process partitioned inputs or claim work through a single enforced ordering boundary, such as a database [uniqueness constraint](https://www.devobs.io/articles/database-constraints-versus-application-validation/), or a compare-and-set update on the record being claimed, together with idempotent effects. A queue acknowledgment alone does not serialize claims or prevent redelivery. A preflight check like “see whether a row exists, then write it” is not enough, because two runs can pass that check before either commits.

Choose `Replace` for freshness-driven work where partial progress is disposable: cache rebuilds, report snapshots, or periodically regenerated search data. If the value comes from having the newest result, replacing stale work is cleaner than letting old and new runs compete.

## What does Kubernetes guarantee, and what does it not?

A CronJob “starts one-time Jobs on a repeating schedule” and a Job represents “one-off tasks that run to completion and then stop,” according to the official [CronJob documentation](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) and [Job documentation](https://kubernetes.io/docs/concepts/workloads/controllers/job/).

That is a scheduling and retry model, not a business-correctness model. Setting `Forbid` reduces overlap for one CronJob. It does not make writes idempotent, coordinate unrelated services, or create an atomic transaction across systems. If a run can be retried, interrupted, or replaced, the application still needs a commit boundary that makes each side effect safe.

## How should deadlines affect the decision?

Use `.spec.startingDeadlineSeconds` to define how late a run may start before Kubernetes skips it. The [CronJob documentation](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) states that after missing this deadline, “the CronJob skips that instance of the Job.”

This matters especially with `Forbid`. If the previous run blocks the next schedule and finally finishes later, Kubernetes still checks the deadline before starting the missed run. So ask two questions together: is overlap unsafe, and is a late run still useful?

Worked example:

- A nightly invoice generator should usually use `Forbid`. Duplicate execution is dangerous, and a late run may still be acceptable if accounting can tolerate delay.
- A five-minute dashboard cache refresh should usually use `Replace`. The newest snapshot matters more than preserving partial work from the previous run.

## What about missed runs and catch-up?

If catching up late still delivers value, set a deadline long enough to absorb controller delay or a short outage. If stale output is useless, keep the deadline short and skip the missed run deliberately. Do not treat catch-up as a substitute for application logic that knows which business period has and has not been processed.

## Questions about CronJob overlap and replacement

**Should I default to `Allow` because it is the Kubernetes default?**  
No. Defaulting to `Allow` is reasonable only when overlapping runs are already safe at the data boundary.

**Does `Replace` preserve progress from the run it interrupts?**  
No documented CronJob behavior guarantees that. Use it only when interrupted work can be discarded or rebuilt safely.

Next step: classify your CronJob as duplicate-sensitive, overlap-safe, or freshness-driven, then set both `concurrencyPolicy` and `startingDeadlineSeconds` explicitly in the manifest.

Reviewed: 2026-09-05

## Source references

- <https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/>
- <https://kubernetes.io/docs/concepts/workloads/controllers/job/>
