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
Stuck on this Kubernetes & Helm error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
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.
Fixed it? Get 500 Kubernetes & Helm & 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.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 4Transport endpoint is not connected
- 5modprobe: FATAL: Module not found
- 6mount: wrong fs type, bad option, bad superblock
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.