1. Introduction
A Kubernetes pod stuck in Terminating is one of the most confusing states to encounter. You've deleted the pod, the delete command returned without error, but minutes later the pod is still there — STATUS: Terminating, sitting in limbo. The node may even be gone, yet the pod persists in your API server's view of the cluster.
Understanding why this happens requires knowing how Kubernetes deletion works. This guide walks through the complete diagnostic and resolution process, from confirming the issue to safely force-deleting when necessary — and how to prevent it from recurring.
2. What "Terminating" Actually Means
When you run kubectl delete pod, Kubernetes doesn't immediately remove the pod record. Instead, it sets a deletionTimestamp on the pod object and gives the pod a grace period (default 30 seconds) to shut down cleanly. During this period the pod shows as Terminating.
The pod only disappears from the API once two things happen: the grace period expires (or the container exits) and all registered finalizers have been removed from the pod's metadata. If either condition is never met, the pod stays Terminating indefinitely.
3. Common Causes
- A finalizer remains on the pod that no controller is processing (orphaned finalizer)
- The node the pod ran on is unreachable — the kubelet can't confirm the container exited
- The container is ignoring SIGTERM and the grace period hasn't expired yet
- A PersistentVolume or PVC is still mounted and the CSI driver is hanging on unmount
- A network policy or webhook is blocking the deletion lifecycle
- The pod has a
terminationGracePeriodSecondsset very high (e.g. 3600) - A service mesh sidecar (Envoy/Istio) is preventing the pod from exiting cleanly
- The pod is in a namespace with a CrashLoopBackOff condition on its node's DaemonSet
4. Step-by-Step Diagnosis and Fix
Step 1: Confirm the stuck state and check how long it has been Terminating
# Get the pod status with age
kubectl get pod <pod-name> -n <namespace>
# A pod stuck for more than 2 minutes warrants investigation
# NAME READY STATUS RESTARTS AGE
# myapp-xyz 0/1 Terminating 0 18m
# Get the full pod spec — key fields: deletionTimestamp, finalizers, node
kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A5 "deletionTimestamp\|finalizers\|nodeName"
Step 2: Check for finalizers
# Check if finalizers are blocking deletion
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.metadata.finalizers}'
# If this returns values like:
# ["foregroundDeletion","some.io/custom-finalizer"]
# Those finalizers must be cleared before the pod deletes.
# Check which controllers are responsible for each finalizer
kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A2 finalizers
Step 3: Check node status
# Find which node the pod is on
kubectl get pod <pod-name> -n <namespace> -o wide
# Check that node's status
kubectl get node <node-name>
# If the node is NotReady or doesn't exist:
# The kubelet can't confirm container exit, so the pod stays Terminating.
# You will need to force-delete (see Step 6).
Step 4: Check termination grace period and container shutdown
# Check terminationGracePeriodSeconds
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.terminationGracePeriodSeconds}'
# View container logs to see if it received SIGTERM
kubectl logs <pod-name> -n <namespace> --previous 2>/dev/null || kubectl logs <pod-name> -n <namespace>
# Check pod events for useful signals
kubectl describe pod <pod-name> -n <namespace> | tail -30
Step 5: Wait or expedite graceful shutdown
# Re-issue the delete with a shorter grace period
# (only if you are sure it's safe to force exit the process)
kubectl delete pod <pod-name> -n <namespace> --grace-period=5
# Or send SIGKILL immediately (grace-period=0 requires --force)
kubectl delete pod <pod-name> -n <namespace> --grace-period=0 --force
Step 6: Remove orphaned finalizers (the most common real fix)
# Patch the pod to remove all finalizers
kubectl patch pod <pod-name> -n <namespace> -p '{"metadata":{"finalizers":[]}}' --type merge
# Verify the pod is now gone (may take a few seconds)
kubectl get pod <pod-name> -n <namespace>
# If multiple pods are stuck with the same finalizer, identify the controller
# that should be managing it and check its logs:
kubectl get pods -n <namespace> | grep Terminating
kubectl logs <controller-pod> -n <controller-namespace>
Step 7: Force-delete when the node is gone
# If the node no longer exists, delete with --force --grace-period=0
kubectl delete pod <pod-name> -n <namespace> --force --grace-period=0
# For multiple stuck pods (use with care):
kubectl get pods -n <namespace> --field-selector=status.phase=Running | grep Terminating | awk '{print $1}' | xargs kubectl delete pod -n <namespace> --force --grace-period=0
5. Verification Steps
# Confirm the pod is gone
kubectl get pod <pod-name> -n <namespace>
# Expected: Error from server (NotFound): pods "pod-name" not found
# Confirm the namespace is clean
kubectl get pods -n <namespace> | grep Terminating
# Expected: no output
# If the pod was part of a Deployment, confirm new pod is healthy
kubectl get pods -n <namespace> -l app=<app-label>
kubectl rollout status deployment/<deployment-name> -n <namespace>
6. Common Mistakes
- Running
--force --grace-period=0before checking for finalizers — if finalizers exist, the pod will still be stuck - Force-deleting pods on a healthy, reachable node — you may leave orphaned processes on the node
- Ignoring the stuck pod — in StatefulSets, a Terminating pod can block new pod scheduling because Kubernetes ensures only one pod per identity runs at a time
- Deleting the namespace instead of the pod — a namespace with stuck pods will itself get stuck in Terminating
- Not investigating why the finalizer was never cleared — if a controller is broken, more pods will get stuck
7. Prevention Tips
- Always implement a SIGTERM handler in your application — catch the signal and shut down gracefully within
terminationGracePeriodSeconds - Set
terminationGracePeriodSecondsto match your actual shutdown time — not the default 30s if your app needs more, and not 3600s if it only needs 10 - If using Istio, add a
preStophook that sleeps briefly to give the sidecar time to finish in-flight requests before the main container exits - Audit finalizers in custom controllers — ensure every finalizer your controller adds has a corresponding removal code path that handles errors
- Monitor for
Terminatingpods with Prometheus usingkube_pod_deletion_timestampand alert if a pod has been terminating for more than 5 minutes - If you rely on node autoscaling, ensure pods have
PodDisruptionBudgetsso nodes drain gracefully before pods are force-terminated
8. FAQ
Will force-deleting a pod cause data loss?
Possibly. If the pod was writing to a PersistentVolume and didn't flush to disk, force-deletion can leave the volume in an inconsistent state. Always check whether the pod's workload is stateful before using --force --grace-period=0.
Why does a Terminating pod come back after I delete it?
If the pod is owned by a controller (Deployment, ReplicaSet, Job), the controller will recreate it. You need to either delete the controller or scale it to zero, not delete the pod directly.
Can a stuck Terminating pod affect other pods?
Yes. In StatefulSets, it blocks new pod creation with the same ordinal. If the pod holds a PVC, the volume may not be releasable to the new pod. A stuck Terminating pod can also keep a node from draining fully during maintenance.
What is the difference between --force and --grace-period=0?
--grace-period=0 tells the kubelet to kill the container immediately (SIGKILL). --force tells the API server to remove the pod object even without confirmation from the kubelet. You typically need both together when the node is unreachable.
9. Summary
A pod stuck in Terminating is almost always caused by either an uncleared finalizer or an unreachable node. Check metadata.finalizers first — if present, patch them away. If the node is gone, use --force --grace-period=0. Never skip investigating why the finalizer wasn't removed automatically, as the root cause will affect future pods too.
| Root cause | Signal | Fix |
|---|---|---|
| Orphaned finalizer | metadata.finalizers not empty | Patch finalizers to [] |
| Node unreachable | Node is NotReady or missing | kubectl delete --force --grace-period=0 |
| Long grace period | High terminationGracePeriodSeconds | Re-delete with --grace-period=5 |
| App ignoring SIGTERM | Container still running after grace period | Fix signal handling; use --grace-period=0 |
| CSI/volume hang | PVC unmount blocked | Investigate CSI driver; patch finalizers if safe |
Explore More in This Category
Explore more in this category: Kubernetes guides. Browse all DevOps Compass articles or jump to a related area: Kubernetes, AWS, CI/CD, Containers, Monitoring, Networking.