<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Cleanup · XB Field Notes</title><link>https://hk.xbcatm.com/tags/cleanup/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://hk.xbcatm.com/tags/cleanup/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes CronJobs need a history policy</title><link>https://hk.xbcatm.com/posts/kubernetes-cronjob-cleanup-hooks/</link><pubDate>Fri, 25 Feb 2028 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/posts/kubernetes-cronjob-cleanup-hooks/</guid><description>CronJob history defaults, Job TTL, ownership and external logs provide different retention controls.</description><content:encoded>&lt;p&gt;A CronJob creates Jobs on a schedule, and each Job manages the Pods that perform the work. Finished objects can preserve useful status, but retention is bounded by several independent mechanisms. In particular, CronJobs already have default history limits. Omitting explicit history settings does not make ordinary completed runs accumulate forever.&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-kubernetes-cronjob-cleanup-hooks.detail.en.svg" alt="CronJob creates Jobs that manage Pods. Finished-history count and TTL age are independent cleanup controls. Ownership affects cascading deletion; active runs and external log retention need separate policies." width="560" height="2820"&gt;&lt;figcaption&gt;Count and TTL do not guarantee minimum retention when the other permits earlier deletion. Cleanup is asynchronous, and active-job limits do not prove a business operation is safe to retry. The retained diagnostics were not rerun.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="separate-history-from-log-retention"&gt;Separate history from log retention&lt;/h2&gt;
&lt;p&gt;The default CronJob history keeps up to three successful Jobs and one failed Job. The controller reconciles those categories independently. A zero limit requests no retained Jobs in that category, but cleanup is asynchronous rather than an exact completion-time deletion. Active Jobs are not completed-history entries and need separate attention.&lt;/p&gt;
&lt;p&gt;Thirty days of hourly scheduling means 720 scheduled opportunities, not necessarily 720 retained Jobs or exactly 720 Pods. Missed schedules, concurrency policy, retries and parallelism change those relationships. A Job can own several Pods. State the assumptions before using run counts as storage or API-capacity estimates.&lt;/p&gt;
&lt;p&gt;Keeping a Pod object also does not guarantee its logs remain available. Node failure, rotation, container cleanup and eviction affect log access. Export the operational evidence required by your retention policy to a suitable logging system. Kubernetes objects are useful investigation records, but they are not an archival guarantee.&lt;/p&gt;
&lt;p&gt;Large retained histories still have costs: object storage, list sizes and investigation noise. A watch receives relevant changes rather than repeatedly emitting every unchanged object merely because it exists. Measure actual object counts and control-plane behavior instead of describing retained status as continuous activity.&lt;/p&gt;
&lt;h2 id="count-and-age-are-independent-policies"&gt;Count and age are independent policies&lt;/h2&gt;
&lt;p&gt;The CronJob history limits constrain finished Job counts, not their minimum age. A busy schedule can replace the last failed record quickly, while a quiet schedule may keep it much longer. Choose counts from the inspection interval and failure frequency, rather than treating one retained failure as enough evidence for every workload.&lt;/p&gt;
&lt;p&gt;TTL-after-finished is configured on the Job. For future CronJob-created Jobs, place ttlSecondsAfterFinished inside the Job template&amp;rsquo;s spec. The timer starts when a Job reaches Complete or Failed; expiry makes it eligible for cascading deletion. A zero TTL makes it eligible immediately, while an unset TTL supplies no age-based cleanup.&lt;/p&gt;
&lt;p&gt;History cleanup and TTL can each remove a Job. Consequently, neither a large history count nor a long TTL guarantees minimum retention when the other mechanism permits earlier deletion. Finalizers and controller progress can delay physical removal. Editing a CronJob template affects future Jobs; it does not rewrite the policy of every existing Job.&lt;/p&gt;
&lt;h2 id="ownership-determines-cascading-behavior"&gt;Ownership determines cascading behavior&lt;/h2&gt;
&lt;p&gt;CronJob-created Jobs reference their owner, and Jobs reference their Pods through dependent ownership. Normal cascading deletion can remove that hierarchy. Explicit orphan deletion instead preserves dependents and removes the relevant ownership link; it does not intentionally leave an ordinary dangling reference for the collector to follow.&lt;/p&gt;
&lt;p&gt;After orphaning Jobs, the former CronJob no longer trims their history. A configured TTL can still apply to each finished Job. Directly created Jobs also need an explicit retention decision. Check the actual deletion propagation policy and ownership rather than assuming every client uses identical defaults or that every orphan will soon disappear.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;A Job that never reaches a terminal condition does not become eligible through finished-job TTL. History limits do not bound active runs either. An appropriate activeDeadlineSeconds can bound a Job&amp;rsquo;s runtime, while concurrencyPolicy controls overlapping schedules. These settings address different failure modes and do not prove the underlying operation is safe to retry.&lt;/p&gt;
&lt;p&gt;Zero history can remove the failure record before anyone investigates it. Preserve required logs and result artifacts elsewhere before shortening retention. Conversely, a missing owner relationship or an absent TTL deserves investigation, not automatic deletion: another controller or retention requirement may deliberately own the cleanup decision.&lt;/p&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;The following retained read-only diagnostics were not rerun in this review. Select a disposable test context and namespace. Compare the stored CronJob with the Jobs it actually owns, their completion conditions and creation times; namespace-wide totals below include unrelated workloads and active Jobs.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl get cronjob hourly-rollup -o jsonpath=&amp;#39;{.spec.successfulJobsHistoryLimit} {.spec.failedJobsHistoryLimit} {.spec.jobTemplate.spec.ttlSecondsAfterFinished}{&amp;#34;\n&amp;#34;}&amp;#39;
kubectl get jobs --sort-by=.metadata.creationTimestamp --no-headers | wc -l
kubectl get pods --field-selector=status.phase=Succeeded --no-headers | wc -l
kubectl get jobs -o jsonpath=&amp;#39;{range .items[*]}{.metadata.name} ttl={.spec.ttlSecondsAfterFinished} owner={.metadata.ownerReferences[0].name}{&amp;#34;\n&amp;#34;}{end}&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line usually shows API-defaulted history values even when the manifest omitted them. Blank output is not proof of unlimited retention; check lookup errors and the complete object. The last line displays only the first owner reference. An absent TTL and owner mean these two particular mechanisms provide no demonstrated cleanup, not that deletion is impossible. Repeat inspection across completed runs, expecting bounded owned histories after reconciliation while active runs and unrelated objects are assessed separately.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/"&gt;Kubernetes Job&lt;/a&gt;, &amp;ldquo;Clean up finished jobs automatically&amp;rdquo; and the TTL mechanism for finished Jobs, checked 2026-10-15.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/"&gt;Kubernetes CronJob&lt;/a&gt;, default history limits and the job template, checked 2026-10-15.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/architecture/garbage-collection/"&gt;Kubernetes garbage collection&lt;/a&gt;, owner references and cascading deletion, checked 2026-10-15.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/"&gt;Primary reference&lt;/a&gt;, TTL eligibility and finalizers, checked 2026-10-15.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/logging/"&gt;Primary reference&lt;/a&gt;, log rotation and node-level retention, checked 2026-10-15.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>