kubectl delete does not delete a pod. It records an intent: the API server stamps .metadata.deletionTimestamp, tracks a grace period, and the pod shows as Terminating while the kubelet does the actual shutdown. The design aim, in the documentation's words, is to let processes terminate gracefully rather than being stopped abruptly with a KILL signal.
The normal sequence is that the kubelet asks the runtime to stop each container by sending SIGTERM — or whatever STOPSIGNAL the image defines, which containerd and CRI-O default to SIGTERM — waits out the grace period, then sends SIGKILL to whatever is still running and removes the pod from the API server. terminationGracePeriodSeconds defaults to 30.
So a pod that has been Terminating for ten seconds is not stuck, it is working. Stuck means it has outlived its grace period by a clear margin, and that only happens for a small set of reasons: a finalizer still on the object, a kubelet that cannot finish tearing the pod down (volume or container-runtime errors), or a kubelet that cannot report back because its node is unreachable. A slow preStop hook only stretches shutdown to the grace period plus 2 seconds.
Finalizers are the reason that surprises people, because they are not about the container at all. A finalizer is a key in metadata.finalizers telling Kubernetes to wait until a controller has done some cleanup. The API marks the object for deletion and returns 202 Accepted, and the object stays in a terminating state until that list is empty. If the controller that owns the key is gone, nothing will ever empty it.
The tempting fix is --force --grace-period=0, and it is worth knowing exactly what it does before reaching for it: the API server stops waiting for the kubelet to confirm anything and removes the object immediately, unless finalizers are still listed: those keep the object in place even through a force delete. The documentation's own caution is blunt — immediate deletion does not wait for confirmation that the running resource has terminated, and the resource may continue to run on the cluster indefinitely. You get a clean kubectl get pods and possibly a container still holding a volume or a lock.