Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 9 min read

Kubernetes Error Guide: Pod Stuck 'Terminating' — Fix Pods That Won't Delete

Quick answer

Fix pods stuck in Terminating status in Kubernetes: diagnose finalizers, unreachable kubelets, stuck volume unmounts, and long grace periods safely.

  • #kubernetes
  • #troubleshooting
  • #errors
  • #pods
Free toolkit

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 Terminating far longer than its terminationGracePeriodSeconds.
  • kubectl get pod -o yaml shows a metadata.deletionTimestamp and often a metadata.finalizers list.
  • The owning controller cannot roll forward because the old pod never releases.
  • On a dead node, many pods on that node all hang Terminating together.
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 terminationGracePeriodSeconds instead of riding out the timeout.
  • Keep terminationGracePeriodSeconds realistic; 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 preStop hooks 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:

  1. A finalizer whose owning controller never clears it.
  2. An unreachable node (NotReady) so the control plane cannot confirm the pod stopped.
  3. A stuck volume unmount/detach.
  4. A container ignoring SIGTERM and waiting out a long grace period.
  5. A preStop hook 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.

Free download · 368-page PDF

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.