✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes CronJob Management

Kubernetes CronJob Management schedules and manages recurring tasks in Kubernetes, ensuring reliable and efficient automated execution.

Kubernetes CronJob Management is the practice of defining, scheduling, and operating recurring batch workloads using the batch/v1 CronJob resource, which wraps a Job template with a cron-formatted schedule and a set of controls governing how repeated executions behave over time. A CronJob does not run workloads itself; it is a factory that creates a new Job object at each scheduled tick, and management of a CronJob is therefore really management of the policies that decide how those generated Jobs are created, allowed to overlap, retained, and cleaned up.

Because CronJobs generate a continuous stream of Job objects over the lifetime of a cluster, disciplined CronJob management is what prevents recurring workloads from silently piling up stale objects, running duplicate executions, or missing scheduled runs during periods of controller unavailability.


Scheduling

Cron Schedule Syntax

.spec.schedule uses standard five-field cron syntax (minute, hour, day-of-month, month, day-of-week), evaluated in the time zone specified by .spec.timeZone (defaulting to the kube-controller-manager's configured time zone if unset, which is a common source of confusion in clusters that assume UTC without setting it explicitly).

spec:
  schedule: "0 2 * * *"
  timeZone: "UTC"

Starting Deadline

.spec.startingDeadlineSeconds bounds how late a missed scheduled run may still be started. If the CronJob controller was unavailable (due to a control plane outage, for instance) for longer than this deadline, the missed run is skipped entirely rather than started late; without this field set, Kubernetes will attempt to start any missed runs going back a limited internal window, which can produce a burst of catch-up Jobs after an outage if not bounded.


Concurrency Control

concurrencyPolicy

.spec.concurrencyPolicy governs what happens when a scheduled run's start time arrives while a previous run's Job has not yet finished:

  • Allow (default): the new Job is created regardless, and both run concurrently.
  • Forbid: the new run is skipped entirely if a previous Job is still active.
  • Replace: the still-running previous Job is terminated and replaced by the new one.

Choosing Forbid is common for workloads that are not safe to run concurrently (a report generator writing to a shared output location, a database migration), while Allow suits independent, idempotent runs where overlap is harmless.

Suspending a CronJob

.spec.suspend: true stops the CronJob controller from creating any new Jobs, without deleting the CronJob object itself or affecting Jobs already created from previous runs. This is the standard way to pause recurring workloads temporarily (during a maintenance window, for example) without losing the schedule configuration.

kubectl patch cronjob codartium-nightly-report -p '{"spec":{"suspend":true}}'

History and Cleanup

successfulJobsHistoryLimit and failedJobsHistoryLimit

These fields cap how many completed and failed Job objects, respectively, are retained per CronJob after execution. Once the limit is exceeded, the oldest Jobs beyond the retained count are deleted automatically by the CronJob controller, independent of any ttlSecondsAfterFinished set on the Job template itself.

spec:
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 5

Retaining a small number of recent failed Jobs is particularly useful operationally, since it lets an on-call engineer inspect the most recent failure's logs without needing external log aggregation, while still bounding overall object growth.


Observability

Inspecting Schedule and Recent Runs

kubectl get cronjob codartium-nightly-report
kubectl get jobs -l app.kubernetes.io/created-by=codartium-nightly-report --sort-by=.status.startTime
kubectl describe cronjob codartium-nightly-report

kubectl describe cronjob surfaces the last scheduled time and last successful time, which are the two fields most useful for quickly confirming whether a CronJob is running on schedule.

Manually Triggering a Run

kubectl create job --from=cronjob/codartium-nightly-report codartium-nightly-report-manual-$(date +%s)

This creates a one-off Job using the CronJob's template without waiting for the next scheduled tick, which is the standard way to test a CronJob's configuration or re-run a missed execution on demand.


Full Example

apiVersion: batch/v1
kind: CronJob
metadata:
  name: codartium-nightly-report
spec:
  schedule: "30 1 * * *"
  timeZone: "UTC"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 5
  suspend: false
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 1200
      ttlSecondsAfterFinished: 86400
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: report
              image: codartium/report-generator:latest
              resources:
                requests:
                  cpu: "250m"
                  memory: "256Mi"