✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Job Cleanup Management

Kubernetes Job Cleanup Management ensures efficient resource use by automatically removing completed jobs, preventing clutter and optimizing cluster performance.

Kubernetes Job Cleanup Management is the set of mechanisms for removing finished batch/v1 Job objects and their associated Pods from a cluster once they are no longer needed, so that clusters running large numbers of batch workloads do not accumulate an unbounded amount of stale object state. Because Jobs are run-to-completion resources rather than long-lived ones, every Job eventually reaches a terminal state, and without deliberate cleanup, both the Job objects themselves and the (by default retained) Pods they created persist indefinitely, consuming etcd storage and cluttering kubectl get output.

Cleanup management balances two competing needs: retaining finished Jobs and Pods long enough for operators to inspect logs, exit codes, and status after a failure or success, while eventually removing them so the cluster does not accumulate garbage over time.


Automatic Cleanup with TTL

ttlSecondsAfterFinished

.spec.ttlSecondsAfterFinished is the primary built-in cleanup mechanism. Once a Job reaches a terminal condition (Complete or Failed), a dedicated TTL controller begins counting down from the configured number of seconds. When the TTL expires, the controller deletes the Job object, which cascades to delete its owned Pods as well.

spec:
  ttlSecondsAfterFinished: 3600

Why It Is Opt-In

The field is left unset by default, meaning Jobs and their Pods are retained forever unless an operator explicitly configures a TTL or deletes them manually. This default favors debuggability: a Job that fails unexpectedly in production should not vanish before anyone has had a chance to inspect its Pods' logs and exit codes.

Choosing a TTL Value

The TTL should be set long enough to comfortably exceed the time it typically takes for monitoring, alerting, or an on-call engineer to notice and investigate a failure, but short enough that routine, expected Job runs (such as those from a frequent CronJob) do not pile up faster than they are cleaned. A common pattern is a TTL of one to a few hours for high-frequency Jobs, and longer (a day or more) for infrequent, high-value batch runs.


CronJob-Specific Cleanup

History Limits

For Jobs created by a CronJob, .spec.successfulJobsHistoryLimit and .spec.failedJobsHistoryLimit independently cap how many completed and failed Job objects are retained per CronJob, regardless of TTL settings. Once the limit is exceeded, the oldest Jobs beyond the retained count are deleted by the CronJob controller. This is typically combined with, rather than used instead of, ttlSecondsAfterFinished on the Job template, giving two independent bounds on retention (count-based and time-based).

apiVersion: batch/v1
kind: CronJob
metadata:
  name: codartium-nightly-report
spec:
  schedule: "0 2 * * *"
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 5
  jobTemplate:
    spec:
      ttlSecondsAfterFinished: 86400
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: report
              image: codartium/report-generator:latest

Manual Cleanup

Deleting a Job and Its Pods

kubectl delete job codartium-batch-import

By default, deleting a Job cascades to delete all Pods it owns, since Pods are created with an owner reference back to the Job.

Orphaning Pods for Inspection

kubectl delete job codartium-batch-import --cascade=orphan

Using --cascade=orphan removes the Job object while leaving its Pods running and intact, detached from any owning controller. This is occasionally used when an operator wants to keep a specific Pod alive for manual debugging (attaching a shell, inspecting a mounted volume) without the Job's TTL or garbage collection interfering, though the orphaned Pods must then be cleaned up manually afterward.

Bulk Cleanup by Label or Age

kubectl delete jobs -l app=codartium --field-selector status.successful=1
kubectl get jobs -o json | jq -r '.items[] | select(.status.completionTime < "2026-07-01") | .metadata.name' | xargs kubectl delete job

Bulk cleanup by label selector or by inspecting .status.completionTime is a common fallback for clusters running many Jobs without TTLs configured, or where a policy change requires purging Jobs older than a certain retention window retroactively.


Log Retention Before Cleanup

Because Pod logs are only accessible while the Pod object still exists (the kubelet retains container logs on disk only briefly after deletion, and only for Pods it hosted), operators relying on TTL-based cleanup for high-volume Jobs typically pair it with a cluster-level log aggregation pipeline (such as a Fluent Bit or Vector DaemonSet shipping to a central log store) so that log data survives Job deletion even though the Kubernetes objects themselves do not.

kubectl logs -l job-name=codartium-batch-import --all-containers > job-output.log

Capturing logs explicitly before an anticipated TTL expiry is a reasonable safeguard for Jobs whose failures are expensive to reproduce.