How to Manage Kubernetes Finalizers and Stuck Resources
Understand and manage finalizers for controlled resource deletion. Handle stuck resources and implement custom cleanup logic.
How to Manage Kubernetes Finalizers and Stuck Resources
Finalizers prevent resources from being deleted until cleanup tasks complete. They’re used by controllers to ensure proper cleanup of dependent resources, but can cause resources to get stuck.
Understanding Finalizers
# Finalizers are metadata strings that block deletion
apiVersion: v1
kind: Namespace
metadata:
name: my-namespace
finalizers:
- kubernetes # Blocks deletion until removed# How deletion works with finalizers:
# 1. kubectl delete issued
# 2. API server sets deletionTimestamp
# 3. Resource enters "Terminating" state
# 4. Controllers see deletionTimestamp, perform cleanup
# 5. Controllers remove their finalizer
# 6. When all finalizers removed, resource is deletedView Finalizers
# Check finalizers on a resource
kubectl get namespace my-namespace -o jsonpath='{.metadata.finalizers}'
# View in YAML
kubectl get pv my-pv -o yaml | grep -A 5 finalizers
# Find resources with specific finalizer
kubectl get namespaces -o json | jq '.items[] | select(.metadata.finalizers != null) | .metadata.name'
# Check deletionTimestamp (indicates deletion in progress)
kubectl get namespace my-namespace -o jsonpath='{.metadata.deletionTimestamp}'
# Find all Terminating namespaces
kubectl get namespaces --field-selector status.phase=TerminatingCommon Finalizers
# Namespace finalizer
kubernetes # Ensures all namespaced resources are deleted first
# PersistentVolume finalizers
kubernetes.io/pv-protection # Prevents deletion while bound to PVC
external-attacher/csi-driver # CSI driver cleanup
# PersistentVolumeClaim finalizers
kubernetes.io/pvc-protection # Prevents deletion while in use by pod
# Custom Resource finalizers
foregroundDeletion # Garbage collection
orphan # Leave dependentsDiagnose Stuck Namespace
# Namespace stuck in Terminating
kubectl get namespace stuck-ns
# Check what's blocking
kubectl get namespace stuck-ns -o yaml
# Look for:
# - finalizers that haven't been removed
# - deletionTimestamp set (deletion started)
# - status.conditions for errors
# Find remaining resources in namespace
kubectl api-resources --verbs=list --namespaced -o name | \
xargs -I {} kubectl get {} -n stuck-ns --ignore-not-found
# Check for stuck API resources
kubectl get apiservices | grep -v AvailableRemove Stuck Finalizers
# WARNING: Only do this after understanding why finalizer exists
# Removing finalizers bypasses cleanup - may leave orphaned resources
# Remove finalizer from namespace (API method)
kubectl get namespace stuck-ns -o json | \
jq '.spec.finalizers = []' | \
kubectl replace --raw "/api/v1/namespaces/stuck-ns/finalize" -f -
# Remove finalizer from other resources
kubectl patch pv my-pv -p '{"metadata":{"finalizers":null}}' --type=merge
# Using kubectl edit
kubectl edit namespace stuck-ns
# Remove the finalizers array and save
# Patch to remove specific finalizer
kubectl patch configmap my-cm -p '{"metadata":{"finalizers":null}}' --type=mergeFix Stuck PV/PVC
# PVC stuck in Terminating (still in use)
kubectl get pods --all-namespaces -o json | \
jq '.items[] | select(.spec.volumes[]?.persistentVolumeClaim.claimName == "my-pvc") | .metadata.name'
# Remove the pod using the PVC first
kubectl delete pod <pod-using-pvc>
# If still stuck, remove finalizer (after confirming no usage)
kubectl patch pvc my-pvc -p '{"metadata":{"finalizers":null}}' --type=merge
# PV stuck after PVC deleted
kubectl patch pv my-pv -p '{"metadata":{"finalizers":null}}' --type=mergeFix Stuck CRDs
# CRD stuck in deletion
kubectl get crd stuck-crd.example.com -o yaml
# Delete all instances of the CRD first
kubectl delete <crd-kind> --all -A
# If controller is gone, remove finalizer
kubectl patch crd stuck-crd.example.com -p '{"metadata":{"finalizers":null}}' --type=mergeForeground vs Background Deletion
# Background deletion (default)
# - Owner deleted immediately
# - Dependents deleted asynchronously
kubectl delete deployment my-deploy
# Foreground deletion
# - Owner waits for dependents to delete
# - Uses foregroundDeletion finalizer
kubectl delete deployment my-deploy --cascade=foreground
# Orphan dependents
# - Owner deleted, dependents kept
kubectl delete deployment my-deploy --cascade=orphanDebug Finalizer Issues
# Check controller logs for finalizer
kubectl logs -n kube-system deployment/kube-controller-manager | grep -i finalizer
# For custom controllers
kubectl logs deployment/my-controller | grep -i cleanup
# Check events
kubectl get events --field-selector involvedObject.name=stuck-resource
# Check if API services are healthy (can block namespace deletion)
kubectl get apiservices | grep FalseAutomated Cleanup Script
#!/bin/bash
# force-delete-namespace.sh
NS=$1
if [ -z "$NS" ]; then
echo "Usage: $0 <namespace>"
exit 1
fi
echo "Checking namespace $NS..."
# Check if namespace exists and is terminating
STATUS=$(kubectl get namespace $NS -o jsonpath='{.status.phase}' 2>/dev/null)
if [ "$STATUS" != "Terminating" ]; then
echo "Namespace is not in Terminating state (status: $STATUS)"
exit 1
fi
# Remove finalizers
echo "Removing finalizers from namespace $NS..."
kubectl get namespace $NS -o json | \
jq 'del(.spec.finalizers)' | \
kubectl replace --raw "/api/v1/namespaces/$NS/finalize" -f -
echo "Done. Check namespace status:"
kubectl get namespace $NSPrevent Stuck Resources
# Set appropriate timeouts in controllers
# Implement robust cleanup logic
# Handle errors gracefully
# Example: Timeout for cleanup operations
spec:
terminationGracePeriodSeconds: 30# Monitor for stuck resources
kubectl get namespaces --field-selector status.phase=Terminating
# Prometheus alert for long-terminating resources
# time() - kube_namespace_status_phase{phase="Terminating"} > 3600Best Practices
1. Understand before removing
- Finalizers exist for a reason
- Removing may leave orphaned resources
- Check what controller owns the finalizer
2. Fix root cause first
- Delete dependent resources properly
- Ensure controllers are running
- Check for unhealthy API services
3. Custom controllers
- Always handle cleanup errors gracefully
- Implement timeouts
- Log cleanup progress
4. Monitor stuck resources
- Alert on Terminating state > threshold
- Regular audit of orphaned resourcesSummary
Finalizers ensure proper cleanup before resource deletion by blocking removal until controllers complete their work. When resources get stuck in Terminating state, diagnose by checking remaining finalizers, dependent resources, and API service health. Remove finalizers only after understanding the implications - this bypasses cleanup and may leave orphaned resources. For stuck namespaces, use the finalize API endpoint. Always try to fix the root cause (delete dependents, restart controllers) before force-removing finalizers.
📘 Go Further with Kubernetes Recipes
Love this recipe? There’s so much more! This is just one of 100+ hands-on recipes in our comprehensive Kubernetes Recipes book.
Inside the book, you’ll master:
- ✅ Production-ready deployment strategies
- ✅ Advanced networking and security patterns
- ✅ Observability, monitoring, and troubleshooting
- ✅ Real-world best practices from industry experts
“The practical, recipe-based approach made complex Kubernetes concepts finally click for me.”
👉 Get Your Copy Now — Start building production-grade Kubernetes skills today!
📘 Get All 100+ Recipes in One Book
Stop searching — get every production-ready pattern with detailed explanations, best practices, and copy-paste YAML.
Want More Kubernetes Recipes?
This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.