Kubernetes CronJob Retention Control
Kubernetes CronJob Retention Control ensures only necessary job results are kept, managing storage and compliance through configured retention policies.
Kubernetes CronJob Retention Control is the specific garbage collection logic the CronJob controller applies to the Job objects it has created over time, determining how many completed Jobs remain visible for inspection before older ones are deleted, and how this bounded retention interacts with Job-level cleanup mechanisms operating independently underneath it.
The Two Retention Limits
successfulJobsHistoryLimit
spec.successfulJobsHistoryLimit bounds how many Jobs that completed successfully are retained; once a new successful Job pushes the count above this limit, the oldest successful Job (by completion time) is deleted, along with its owned Pods through normal garbage collection cascading.
apiVersion: batch/v1
kind: CronJob
metadata:
name: retention-control-example
spec:
schedule: "*/15 * * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: registry.example.com/worker:1.0.0
failedJobsHistoryLimit
spec.failedJobsHistoryLimit applies the identical logic to Jobs that ended in a Failed state, tracked as a separate count from successful ones, since operators commonly want to retain more failure evidence for debugging than routine successful run history.
Zero as a Valid Value
Immediate Cleanup
Setting either limit to 0 causes the controller to delete a Job of that outcome type immediately upon completion, retaining no history at all for that category, appropriate for high-frequency CronJobs where individual run history has little diagnostic value and namespace object count matters more.
spec:
successfulJobsHistoryLimit: 0
Interaction With ttlSecondsAfterFinished
Two Independent Cleanup Paths
If the Job template itself sets ttlSecondsAfterFinished, that Job-level TTL controller can delete a completed Job before the CronJob's history limit would have, since the two mechanisms operate independently and whichever fires first determines actual removal. A short TTL can effectively make successfulJobsHistoryLimit moot, since Jobs disappear before enough of them accumulate to exceed the limit.
jobTemplate:
spec:
ttlSecondsAfterFinished: 600
Choosing Between the Two Mechanisms
ttlSecondsAfterFinished cleans up based on elapsed time since completion, useful for a fixed retention window regardless of run frequency, while successfulJobsHistoryLimit retains a fixed count regardless of how much time has passed, useful for guaranteeing a minimum number of recent runs remain inspectable even for infrequent schedules.
Retention Does Not Cover Manually Created Jobs
Scope Limited to Controller-Created Objects
Retention limits apply only to Jobs the CronJob controller itself created and owns through ownerReferences; a Job created manually, even if using an identical template, labels, and namespace, is entirely outside this accounting and will not be counted toward or cleaned up by the CronJob's history limits.
kubectl get jobs -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.ownerReferences[0].name}{"\n"}{end}'
Namespace Accumulation Risk
Consequences of Overly Generous Limits
Setting history limits too high on a frequently scheduled CronJob can lead to substantial accumulation of retained Job and Pod objects over time, increasing etcd storage pressure and slowing list operations against the namespace; retention values should be sized deliberately against actual schedule frequency rather than left at defaults without consideration.
Retention Control Diagram
Tuning these two limits, and understanding their independence from any Job-level TTL, is the primary lever for balancing operational visibility into recent CronJob history against the storage and API overhead of retaining that history indefinitely.