Kubernetes Error Guide: Pod Stuck 'Terminating' — Fix Pods That Won't Delete
Fix pods stuck in Terminating status in Kubernetes: diagnose finalizers, unreachable kubelets, stuck volume unmounts, and long grace periods safely.
- #kubernetes
- #troubleshooting
- #errors
- #pods
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
You delete a pod, but it hangs in Terminating and never disappears. Kubernetes marks a pod for deletion by setting deletionTimestamp, then waits for the graceful shutdown to finish and for any finalizers to be removed before the API server actually deletes the object. If any step in that chain stalls — a finalizer that is never cleared, a node whose kubelet is unreachable, a volume that will not unmount, or a container ignoring SIGTERM — the pod sits in Terminating indefinitely.
You will see it like this:
NAME READY STATUS RESTARTS AGE
worker-6c7d9f4b8-nq2ll 1/1 Terminating 0 3h
The pod already has a deletionTimestamp, so it is “being deleted,” but the actual removal is blocked. Force-deleting is tempting but can be dangerous for StatefulSets (risking two pods with the same identity writing to the same volume). The correct move is to find why it is stuck first, then choose the safe remedy.
Symptoms
- Pod stays
Terminatingfar longer than itsterminationGracePeriodSeconds. kubectl get pod -o yamlshows ametadata.deletionTimestampand often ametadata.finalizerslist.- The owning controller cannot roll forward because the old pod never releases.
- On a dead node, many pods on that node all hang
Terminatingtogether.
kubectl get pod worker-6c7d9f4b8-nq2ll
NAME READY STATUS RESTARTS AGE
worker-6c7d9f4b8-nq2ll 1/1 Terminating 0 3h
kubectl get pod worker-6c7d9f4b8-nq2ll -o jsonpath='{.metadata.finalizers}{"\n"}{.metadata.deletionTimestamp}'
["kubernetes.io/pvc-protection"]
2026-07-09T09:14:22Z
Common Root Causes
1. A finalizer that is never removed
If the pod has a metadata.finalizers entry, the API server will not delete it until that finalizer is cleared by its owning controller. A broken or uninstalled controller leaves the finalizer forever.
kubectl get pod <POD> -o jsonpath='{.metadata.finalizers}'
["example.com/cleanup-hook"]
The finalizer name tells you which controller must run. If that controller is gone, the pod is stuck.
2. The node is unreachable (kubelet down)
If the node hosting the pod is NotReady/unreachable, the control plane cannot confirm the pod actually stopped, so it will not finalize deletion. All pods on that node hang together.
kubectl get node <NODE>
NAME STATUS ROLES AGE VERSION
node-7 NotReady <none> 40d v1.31.2
Until the node recovers or is removed, the kubelet cannot report the pods as gone.
3. Volume detach/unmount is stuck
A pod using a PVC will not finish terminating if the CSI driver cannot unmount/detach the volume — often because the backend is unavailable or a VolumeAttachment is wedged.
kubectl describe pod <POD> | grep -A3 -i 'unmount\|detach'
kubectl get volumeattachment | grep <PV>
Warning FailedUnMount Unable to unmount volumes ... timed out waiting for the condition
4. The container ignores SIGTERM and rides out the grace period
If the app does not handle SIGTERM, it keeps running until terminationGracePeriodSeconds elapses and only then gets SIGKILL. A very long grace period (e.g. hours) makes the pod appear “stuck” when it is actually waiting as configured.
kubectl get pod <POD> -o jsonpath='{.spec.terminationGracePeriodSeconds}'
3600
5. A preStop hook that never returns
A lifecycle.preStop hook that blocks (waiting on an endpoint that never responds) holds termination until the grace period forces a kill.
Diagnostic Workflow
Step 1: Confirm deletionTimestamp and grace period
kubectl get pod <POD> -o jsonpath='{.metadata.deletionTimestamp} {.spec.terminationGracePeriodSeconds}{"\n"}'
If the pod has been terminating far longer than the grace period, something is actively blocking it.
Step 2: Check for finalizers
kubectl get pod <POD> -o jsonpath='{.metadata.finalizers}'
A non-empty list means a controller must clear it. Identify and check that controller before removing anything.
Step 3: Check the node’s health
kubectl get node -o wide
kubectl describe node <NODE> | grep -A3 Conditions
A NotReady/unreachable node explains a whole batch of stuck pods.
Step 4: Look for volume unmount problems
kubectl describe pod <POD> | grep -i 'unmount\|detach\|FailedUnMount'
kubectl get volumeattachment
Step 5: Inspect events and the preStop path
kubectl get events --field-selector involvedObject.name=<POD> --sort-by='.lastTimestamp'
kubectl get pod <POD> -o jsonpath='{.spec.containers[0].lifecycle.preStop}'
Example Root Cause Analysis
A rolling update wedges because an old pod will not terminate:
kubectl get pods -l app=worker
NAME READY STATUS RESTARTS AGE
worker-6c7d9f4b8-nq2ll 1/1 Terminating 0 2h
worker-79c5b6f7d9-abcde 1/1 Running 0 2h
The pod has been terminating for two hours with a 30-second grace period, so it is genuinely blocked. Checking finalizers and the node:
kubectl get pod worker-6c7d9f4b8-nq2ll -o jsonpath='{.metadata.finalizers}{"\n"}{.spec.nodeName}'
[]
node-7
No finalizer — so it is not a finalizer hold. The node is the suspect:
kubectl get node node-7
NAME STATUS ROLES AGE VERSION
node-7 NotReady <none> 41d v1.31.2
node-7 is NotReady (a hardware failure took the kubelet offline). The control plane cannot confirm the pod stopped, so it will not finalize the deletion. The safe fix is to remove the dead node from the cluster, which lets the control plane reap its pods:
kubectl delete node node-7
After the node is deleted, the stuck pod is finalized and the rollout completes. Only if the pod must be cleared before the node is handled (and the workload is not a StatefulSet with a shared volume) would you force-delete:
kubectl delete pod worker-6c7d9f4b8-nq2ll --grace-period=0 --force
Prevention Best Practices
- Handle SIGTERM in your application so it shuts down within a sensible
terminationGracePeriodSecondsinstead of riding out the timeout. - Keep
terminationGracePeriodSecondsrealistic; hours-long values make normal shutdowns look like hangs. - Monitor node readiness and drain/remove dead nodes promptly so their pods can be reaped.
- Ensure controllers that add finalizers are healthy and have RBAC to remove them; a dead controller strands every object it finalizes.
- Avoid
preStophooks that can block indefinitely; give them their own timeout. See more in Kubernetes & Helm guides.
Quick Command Reference
# Confirm it is really stuck (past grace period)
kubectl get pod <POD> -o jsonpath='{.metadata.deletionTimestamp} {.spec.terminationGracePeriodSeconds}{"\n"}'
# Check for finalizers holding it
kubectl get pod <POD> -o jsonpath='{.metadata.finalizers}'
# Check the node's health
kubectl get node <NODE>
kubectl describe node <NODE> | grep -A3 Conditions
# Look for volume unmount/detach problems
kubectl describe pod <POD> | grep -i 'unmount\|detach'
kubectl get volumeattachment
# Remove a dead node so its pods are reaped
kubectl delete node <NODE>
# Last-resort force delete (NOT for StatefulSets on shared volumes)
kubectl delete pod <POD> --grace-period=0 --force
Conclusion
A pod stuck Terminating has a deletionTimestamp but something is blocking final removal. The usual causes:
- A finalizer whose owning controller never clears it.
- An unreachable node (
NotReady) so the control plane cannot confirm the pod stopped. - A stuck volume unmount/detach.
- A container ignoring SIGTERM and waiting out a long grace period.
- A
preStophook that never returns.
Check finalizers and node health before reaching for --force — a blind force-delete on a StatefulSet can corrupt data by allowing two pods to share one volume. For ad-hoc triage, the free incident assistant can turn a stuck-terminating describe dump into the safe remedy.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.