Automatic Cleanup for Finished Jobs
Who deletes a Job once it has finished? A Complete or Failed Job and its Pods stay in the API so the run is still inspectable, and .spec.ttlSecondsAfterFinished starts a clock when the Job finishes rather than when it was created. Once that many seconds pass the ttl-after-finished controller deletes the Job cascadingly, so the Pods go with it, while an unset field means this controller never touches the Job and 0 makes it eligible the moment it finishes.
Play the animation 5 steps, animated, best on a wide screen
Automatic Cleanup for Finished Jobs, step by step
-
Both Pods exit 0, so the Job condition becomes Complete and .status.completionTime is stamped at that instant. Finishing removes nothing. The Job object and both Pods stay in the API by default, so kubectl logs and kubectl describe still answer after the run is over. A CronJob prunes its own finished Jobs by count, a bare Job like this one has no such limit.
Play from step 1 -
The ttl-after-finished controller inside kube-controller-manager watches Jobs, and it only looks at ones that have already finished. This Job now reports Complete, so it is picked up. A Job that is still running is one it passes over whatever the spec says. Set the field to 0 and a Job is eligible the moment it finishes, and leave it unset and this controller never touches the Job at all.
Play from step 2 -
The timer starts when the Job finishes, not when it was created. With ttlSecondsAfterFinished at 100, the Job becomes eligible for removal 100 seconds after the completion stamp of 12:00:31, which is 12:02:11. It is 12:01:04 now, 33 of those seconds have gone, and nothing happens yet.
Play from step 3 -
At 12:02:11 the controller compares its own clock against the timestamp stored in the Job, finds the TTL expired and deletes the Job through the API. That comparison makes the TTL sensitive to time skew: clocks should differ very little, but skew in the cluster can make the control plane clean a Job up at the wrong time.
Play from step 4 -
The delete asks for propagationPolicy Foreground, a cascading deletion. Both Pods carry an ownerReference back to Job pi, so the garbage collector deletes them first and removes the Job object only once they are gone. Neither Pod was counted or aged on its own here. They go because their owner is being deleted, the run leaves kubectl get pods, and the Node is left with nothing of this run on it.
Play from step 5