Avoid multiple cron jobs running for one cron execution point in Kubernetes
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Overlapping Kubernetes CronJob executions can lead to duplicate processing, conflicting writes, and unnecessary cluster load. The scheduler will happily start new jobs unless you tell it otherwise, so preventing overlap requires explicit policy. In production, the right solution combines CronJob configuration with workload-level idempotency and reasonable runtime limits.
Why Multiple Runs Happen
The most common cause is simple: the previous run is still active when the next schedule fires.
Typical reasons:
- The job takes longer than its schedule interval.
- Retries keep the job alive.
- External services are slow.
- Manual reruns overlap with the regular schedule.
Kubernetes is not misbehaving in this case. It is following the schedule you defined.
concurrencyPolicy Is the First Control
The main built-in safeguard is concurrencyPolicy.
The available values are:
- '
Allow, which permits overlap.' - '
Forbid, which skips the new run if a previous one is still active.' - '
Replace, which terminates the older run and starts the new one.'
For most stateful workloads, Forbid is the safest choice.
Add Runtime Boundaries
Even with Forbid, you should limit how long a job can keep the schedule blocked.
These settings help with two problems:
- Very late starts after controller disruption.
- Jobs that keep retrying and blocking future runs.
Know When Replace Is Better
Not every job wants Forbid. Some jobs only care about the latest schedule point.
Examples:
- Periodic cache refresh.
- Polling-style metadata sync.
- Non-critical aggregation where old work is obsolete once a new window starts.
In those cases, Replace may be the better operational choice because it preserves “latest only” semantics instead of letting an old run consume time.
Idempotency Still Matters
Scheduler policy reduces overlap, but it does not eliminate all duplicate-execution scenarios. Manual restarts, cluster recovery behavior, or application-level retries can still cause the same logical work to happen more than once.
That is why the job itself should be idempotent where possible:
- Use unique execution markers.
- Upsert rather than blindly insert.
- Make writes atomic.
- Check whether a work unit was already processed.
Cluster configuration is not a substitute for safe workload design.
When External Locking Is Worth It
For high-impact jobs, you may still want an external lock in Redis, Postgres, or another durable store.
Minimal Redis-style example in Python:
This should be used carefully. Complex locking schemes create their own failure modes if TTL and cleanup are not thought through.
Monitor for Near-Overlap, Not Just Failures
Do not wait until duplicates cause incidents. Track:
- Active job count.
- Job runtime percentiles.
- Missed schedule count.
- Retry frequency.
If average runtime is approaching the schedule interval, you already have an overlap risk even if no duplicate execution has happened yet.
Common Pitfalls
- Leaving
concurrencyPolicyat the defaultAllowfor non-idempotent jobs. Fix by explicitly settingForbidorReplace. - Scheduling jobs more frequently than they can realistically finish. Fix by matching interval to runtime budget.
- Assuming scheduler policy alone prevents all duplicate work. Fix by making the job logic idempotent.
- Letting retries run for too long. Fix by setting
backoffLimitandactiveDeadlineSeconds. - Adding distributed locks without a clear failure and TTL model. Fix by keeping lock design simple and operationally testable.
Summary
- CronJob overlap happens when schedule frequency and job runtime are misaligned.
- '
concurrencyPolicyis the primary Kubernetes control for overlapping runs.' - '
Forbidis usually safest for stateful workloads, whileReplaceis better for latest-only jobs.' - Runtime deadlines and retry limits keep schedules predictable.
- Idempotent job logic is still required because scheduler settings are not the whole reliability story.

