✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes CronJob Missed Run Management

Kubernetes CronJob Missed Run Management ensures critical tasks run reliably, addressing scheduling gaps in containerized environments.

Kubernetes CronJob Missed Run Management is the set of behaviors and configuration options that determine how a batch/v1 CronJob handles scheduled ticks that could not be started at their intended time — most commonly because the kube-controller-manager, and specifically the CronJob controller within it, was unavailable when one or more schedule times passed. Rather than treating every missed tick identically, Kubernetes gives operators explicit control over whether a missed run is caught up later, skipped outright, or bounded to a limited catch-up window, and understanding this behavior is essential for any CronJob whose correctness depends on every scheduled period actually being processed exactly once.

A missed schedule is fundamentally a reconciliation problem: the CronJob controller periodically checks whether the current time has passed one or more schedule times since it last checked, and if the controller itself was down (or the whole cluster's control plane was unavailable) across that gap, it must decide retroactively what to do about the ticks that passed unobserved.


Why Runs Get Missed

Controller Downtime

The CronJob controller runs as part of kube-controller-manager. If that component is restarted, crashes, or is otherwise unavailable — during a control plane upgrade, a leader election handoff, or an outage — any schedule ticks that occur during the gap are, by definition, not observed in real time.

Clock and Reconciliation Delay

Even without an outage, the CronJob controller reconciles on a polling interval rather than reacting to schedule ticks with perfect real-time precision; under heavy API server load or controller-manager resource pressure, reconciliation can lag behind the wall clock by more than expected, which can also produce a small number of "late" observations that the missed-run logic must account for.


Controlling Missed Run Behavior

startingDeadlineSeconds

.spec.startingDeadlineSeconds is the primary control: it defines how far in the past a missed schedule time is still allowed to be started. If the controller resumes and finds a missed tick older than this deadline relative to the current time, that specific missed run is skipped entirely and never started — it is not queued, delayed, or run late, simply dropped, and Kubernetes records this as a missed schedule.

spec:
  startingDeadlineSeconds: 300

A startingDeadlineSeconds of 300 on a CronJob scheduled every 5 minutes means a missed tick can be caught up if the controller recovers within 5 minutes of the intended time, but not later than that.

Behavior Without a Deadline Set

If startingDeadlineSeconds is left unset, Kubernetes falls back to an internal bound (historically, no more than 100 missed schedule times are caught up in a single reconciliation pass) rather than attempting unbounded catch-up. This prevents a very frequent schedule (for example, every minute) combined with a long outage from producing an enormous burst of simultaneous Job creations once the controller recovers, but it also makes the exact catch-up behavior less predictable than explicitly setting a deadline.

concurrencyPolicy Interaction

When multiple missed ticks are eligible to be caught up simultaneously, concurrencyPolicy still governs whether they can run concurrently: Forbid will only start the next eligible missed tick once any currently active Job finishes, effectively serializing catch-up runs one at a time rather than launching them all at once; Allow permits them to run in parallel; Replace will cancel an in-progress catch-up run in favor of a more recent one.


Designing for Missed Runs

When Catch-Up Is Desired

For workloads where every period's data must eventually be processed (a financial reconciliation job, an hourly aggregation feeding a report), a generous startingDeadlineSeconds combined with concurrencyPolicy: Forbid ensures missed periods are eventually processed sequentially rather than silently dropped, at the cost of the workload needing to tolerate running later than its nominal schedule time.

When Skipping Missed Runs Is Preferable

For workloads representing a point-in-time snapshot rather than a period that must be reconciled (a "current system status" poll, a cache-warming job), a short or default startingDeadlineSeconds is appropriate, since a stale catch-up run for a past moment in time provides little value and may even be misleading if consumed as if it reflected the present.

Alerting on Missed Schedules

Because a skipped missed run produces no Job object at all (there is nothing to inspect after the fact — it simply never happened), monitoring for missed schedules typically requires comparing .status.lastScheduleTime against the expected cadence externally, rather than relying on any in-cluster record of the skip itself.

kubectl get cronjob codartium-nightly-report -o jsonpath='{.status.lastScheduleTime}'

A monitoring check comparing this timestamp against the expected schedule interval, alerting if it falls too far behind, is the standard way to detect that runs are being missed before it becomes a larger data-completeness problem.


Example

apiVersion: batch/v1
kind: CronJob
metadata:
  name: codartium-missed-run-example
spec:
  schedule: "0 * * * *"
  startingDeadlineSeconds: 600
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: hourly-aggregate
              image: codartium/hourly-aggregate:latest