Kubernetes 1.31.1 preserves completed pods across kubelet restart
The first 1.31 maintenance release fixes a kubelet path that could reconsider admission for a completed pod. Its regression test shows why finished work must remain finished when device availability changes.
A pod that has finished successfully should not become failed because its node later loses access to a device plugin. Kubernetes 1.31.1 fixes a kubelet restart path that could produce that result. The release published on September 12 is the first maintenance update for the 1.31 line. One of its most instructive changes concerns the interpretation of state the node rediscovers after restarting.
The 1.31.1 changelog says terminated pods will no longer be readmitted on kubelet restart. It identifies completed pods awaiting finalizer removal as a case that could otherwise become failed. The bug is about applying a startup decision to an object whose work has already ended. Current resource availability should not retroactively determine whether that earlier execution succeeded.
I value Kubernetes's ability to converge after a component interruption. That depends on interpreting recovered state correctly. A reconciliation process that repeats a decision appropriate only to an earlier lifecycle stage can change the meaning of an object even when the application itself has done nothing new.
Admission needs lifecycle context
The backported patch changes HandlePodAdditions in kubelet. Previously, the condition avoided admission work when the pod worker had requested termination. This repair adds a check that the pod's phase is not terminal. Those conditions describe different sources of knowledge about the pod.
A worker's termination request belongs to the kubelet's execution machinery. The phase records the pod's lifecycle state in the object kubelet is handling. After a restart, the terminal phase is enough to establish that the pod should not reenter admission. Requiring both conditions to permit new admission prevents kubelet from treating a finished pod as another candidate for startup.
The small condition carries a substantial semantic distinction. The object may remain present because cleanup is incomplete, while the workload has already completed. Presence in the API does not imply that the node should try to satisfy the resource requirements for running it again. The patch recognizes that distinction before entering the admission path.
This is also why the change belongs in maintenance work even though it touches ordinary control flow. The intended lifecycle has not changed. The patch restores it under a restart scenario where the previous condition lacked enough information. Operators receive a correction to how the node handles an existing state, rather than a new workload option to configure.
The regression test removes the original resource
The associated node test constructs a stronger case than simply restarting kubelet while everything remains available. It starts a pod requesting a sample device resource, uses a restart policy of Never, and waits for the pod to succeed. A finalizer keeps the object available through the rest of the test. This deliberately separates completed execution from eventual object deletion.
The test then deletes the device-plugin pod and waits until the node reports no allocatable capacity for the sample resource. That removes a condition that was necessary when the workload originally started. The node's remaining capacity information is checked separately. The distinction between capacity and allocatable resources makes the scenario more precise than merely observing that the plugin process stopped.
Only then does the test restart kubelet. After the node becomes ready again, it checks consistently that the pod remains in the succeeded phase. The finalizer is removed afterward so cleanup can proceed. A source comment explains that the earlier bug could attempt readmission and change the phase to failed.
I find that test useful because it changes the environment after the relevant work is complete. A test with the plugin still available might allow mistaken readmission to succeed and conceal the lifecycle error. Removing the resource makes the distinction observable without asking the test to inspect every internal admission call.
A patch release repairs several recovery paths
The changelog also fixes init containers failing to start after temporary runtime failures. Another item corrects scheduler preemption when victim and preemptor status contain different quality-of-service classes. Kube-proxy receives a repair for an initialization race that could blackhole UDP traffic to a Service virtual IP. These are different components, but each release-note entry describes a concrete failure in an existing execution path.
There is also a compatibility adjustment to DisableNodeKubeProxyVersion. The patch release returns that gate to default-off to preserve the documented deprecation interval before clearing the field by default. This shows that maintenance can correct a release's timing assumptions as well as implementation defects. Consumers of an old field still depend on the project's deprecation rules.
The completed-pod repair is the clearest example of the operational value in this update. Kubelet must reconstruct what remains to be done without reopening decisions that belong to a finished execution. The terminal-phase check and the device-plugin regression test establish that a retained pod object can remain meaningful after both a node-component restart and a change in the resources that originally made the workload possible.