KubernetesVII · Declarative Resource ManagementDeclarative resource management
kubectl delete — propagation, foreground, background, orphans
What you'll learn
- Explain foreground, background, and orphan propagation policies
- Identify when to use each propagation mode
- Diagnose objects that refuse to delete due to finalizers
- Apply production discipline around deletion blast radius
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
kubectl delete is the most dangerous command in the operator’s
toolkit. It cascades by default, and it can block on finalizers
silently. This lesson covers the three propagation modes, how
garbage collection works, and the production discipline that
keeps deletion from becoming an outage.
The three propagation modes
kubectl delete deployment web # default: background
kubectl delete deployment web --cascade=background # explicit default
kubectl delete deployment web --cascade=foreground # wait for dependents
kubectl delete deployment web --cascade=orphan # preserve dependents
The --cascade flag (formerly --cascade=true|false; now
background|foreground|orphan) controls what happens to
objects that are owned by the deleted object.
Background (default): kubectl sends the DELETE request and returns immediately. The garbage collector walks the ownership tree asynchronously and deletes dependents in the background. kubectl does not wait.
Foreground: kubectl sends the DELETE request but the garbage collector marks the object as “being deleted” and deletes dependents first. The API server holds the request until all dependents are gone, then the parent is deleted.
Orphan: kubectl sends the DELETE request and the
dependents are removed from the parent’s ownerReferences
but not deleted. The dependents survive; they now have no
owner (or a different owner if re-attached).
flowchart TD
Parent[Deployment web] -->|owns| ReplicaSet[ReplicaSet web-7c8]
ReplicaSet -->|owns| Pod[Pod web-7c8-abc]
B[Background: delete all async] -.->|after| Parent
F[Foreground: delete children first] -.->|before| Parent
O[Orphan: detach children, no delete] -.->|detach| Parent
When to use each mode
Background (default): the standard case. Deleting a Deployment should also delete its ReplicaSets and Pods. The cascading happens asynchronously; kubectl returns immediately.
Foreground: when you need the deletion to be visible and atomic. Use cases:
- Deleting a CRD whose CRs have finalizers that must run.
- Deleting a Helm release whose hooks need to be torn down in order.
- When you want to verify the deletion before the parent is removed.
Foreground is slower but more predictable.
Orphan: when you want to preserve dependents. Use cases:
- Promoting a Pod out from under a ReplicaSet (so the Pod is not deleted when the ReplicaSet is scaled down).
- Migrating a StatefulSet’s PVCs to a new StatefulSet.
- Recovery scenarios: detach children so you can edit the parent safely without losing the children.
Garbage collection under the hood
Kubernetes implements owner-based garbage collection as a
controller in the controller-manager. It watches for the
removal of objects and walks the ownerReferences tree.
flowchart LR
API[API server] -->|mark deleted| GC[Garbage collector]
GC -->|find ownerReferences| Children[Children]
GC -->|cascade delete| Children
GC -->|mark parent deleted| Parent[Parent]
The API server marks the parent object with a deletion
timestamp (metadata.deletionTimestamp). The garbage collector
sees this timestamp and starts deleting dependents. For
foreground propagation, the controller holds the parent’s
finalizers until the children are gone.
When all dependents are gone and all finalizers are removed, the parent object is actually removed from etcd. Until then, the object exists in a “being deleted” state — it is read-only and excluded from most queries.
Finalizers and stuck deletes
A finalizer is a string key in metadata.finalizers. When
present, the API server will not actually delete the object
until the finalizer is removed. Finalizers are how controllers
get a chance to do cleanup work (release external resources,
notify external systems) before the object disappears.
kubectl get pod web-7c8 -o yaml | grep -A 5 finalizers
A Pod stuck in Terminating:
kubectl get pod web-7c8
# NAME STATUS AGE
# web-7c8 Terminating 5m
kubectl get pod web-7c8 -o yaml | grep -A 3 finalizers
# finalizers:
# - kubernetes.io/pv-protection
The Pod is being deleted but the pv-protection finalizer is
blocking it because the Pod’s volumes have not been released.
The fix is to release the volumes first, then the finalizer
will be cleared automatically.
Common finalizers:
kubernetes.io/pv-protection— blocks PVC deletion while Pods reference it.kubernetes.io/pvc-protection— blocks PV deletion while PVCs reference it.foregroundDeletion— set by the foreground propagation cascade.- Custom finalizers added by controllers, CRDs, service meshes, etc.
Forcing a deletion past finalizers:
kubectl delete pod web-7c8 --grace-period=0 --force
kubectl delete pod web-7c8 --grace-period=0 --force --cascade=orphan
--force removes the finalizers from the API server before
the deletion completes. This bypasses the cleanup logic that
the finalizer was supposed to run. This is dangerous: the
external resource (a PVC, a cloud LB, a CSI volume) is left
orphaned. Use it only when:
- The finalizer’s controller is broken (no chance of recovery).
- The associated external resource has been manually cleaned up.
- You have documented the cleanup and understand the consequences.
Deletion timestamps and graceful termination
kubectl delete sends a graceful termination request by
default. The kubelet receives the request, sets the Pod’s
deletion timestamp, runs any preStop hooks, and sends
SIGTERM to the containers. After the
terminationGracePeriodSeconds (default 30s), the kubelet
sends SIGKILL and removes the Pod.
The grace period is configurable:
kubectl delete pod web-7c8 --grace-period=60
kubectl delete pod web-7c8 --grace-period=0 --force # immediate SIGKILL
For Pods with sidecars, the grace period must account for the
sidecar’s shutdown time. Production discipline: set
terminationGracePeriodSeconds on the Pod to account for the
slowest container’s shutdown.
Deletion blast radius
A single delete can cascade to many objects. The production discipline:
- Confirm what will be deleted before the command.
This shows who owns the object. To see what the object owns:# Substitute your own values before running: KIND=deployment NAME=checkout-api kubectl get "$KIND"/"$NAME" --output=jsonpath='{.metadata.ownerReferences}' | jq# Substitute your own values before running: KIND=deployment NAME=checkout-api CHILD_KIND=replicaset kubectl get "$KIND"/"$NAME" -o json | jq '.metadata.uid, [.metadata.name]' # Then for each kind, matching on the parent uid printed above: PARENT_UID=$(kubectl get "$KIND"/"$NAME" -o jsonpath='{.metadata.uid}') kubectl get "$CHILD_KIND" -A -o json | jq --arg uid "$PARENT_UID" '.items[] | select(.metadata.ownerReferences[].uid==$uid) | .metadata.name' - Delete in dependency order when possible. Delete Pods, then ReplicaSets, then Deployments. This is slower than cascade but predictable.
- Use
--dry-run=serverto confirm the request is accepted before sending it. - Capture the object before deletion. A deletion is irreversible except via etcd restore.
Cross-course references
- The Linux course part
XXII-Linux-NetTroubleshootcovers cleanup discipline at the OS level; the same logic applies to cluster objects. - The Ansible course part
XXXVI-Ansible-Driftcovers cleanup of drifted state; cluster garbage collection is the Kubernetes equivalent. - The Terraform course part
XXVIII-Terraform-Disaster-Recoverycovers recovery from destructive operations; etcd snapshot restore is the equivalent for cluster state.
Quiz
Knowledge check · 4 questions
Q1. Which propagation mode is the default for `kubectl delete`?
Q2. If a Pod has a finalizer and is stuck in `Terminating`, the only way to delete it is `kubectl delete --force`.
Q3. An operator runs `kubectl delete statefulset web` to remove a 3-replica StatefulSet. The operator expects the Pods to be deleted but the PVCs to be preserved (because the data needs to be retained). Diagnose what happens, and how to get the desired outcome.
StatefulSet `web` has 3 replicas (`web-0`, `web-1`, `web-2`). Each Pod has a PVC named `data-web-<n>` backed by a StorageClass with `reclaimPolicy: Delete`. The operator wants: - Pods gone (yes) - ReplicaSet/controller gone (yes) - PVCs preserved (yes) - Data preserved (yes)
Q4. What is a finalizer, and what happens if you remove it before the controller has finished its cleanup work?
Passing score: 75%. Answers are checked in this browser.
Production discipline
- Use
--cascade=orphanwhen you want to preserve dependents. Promotions, migrations, recovery scenarios. - Foreground propagation for predictable deletion. When you need the deletion to be atomic and ordered.
- Investigate finalizers before forcing past them. A Pod
stuck in
Terminatingis signalling a real problem; the fix is to clear the resource the finalizer is protecting, not to bypass it. - Capture the object before deletion. A deletion is
irreversible except via etcd restore.
kubectl get -o yamlis the cheap insurance. - Make deletion a runbook step. Ad-hoc deletions in production are an outage risk. The runbook entry should cover cascade, blast radius, rollback, and verification.