skip to content

In Kubernetes, what happens to a Deployment's ReplicaSets and Pods when you run kubectl delete deployment, and how does --cascade=orphan change that?

level: juniorimportance: must knowfreq 72%

answer

  1. the child points at the parent
  2. a controller in kube-controller-manager
  3. background is the kubectl default
  4. orphan finalizer strips references first
  5. only direct children are detached

basics

~20 s

kubectl delete deployment removes the Deployment, then the garbage collector deletes its ReplicaSets and their Pods, which point at their owners through ownerReferences. With --cascade=orphan, those references are removed instead, and the ReplicaSets and Pods keep running.

solid answer

~40 s

Every ReplicaSet a Deployment creates carries a `metadata.ownerReferences` entry naming that Deployment by name and UID, and each Pod names its ReplicaSet the same way. `kubectl delete` defaults to `--cascade=background`: the API server removes the Deployment at once, and the garbage collector in `kube-controller-manager` then deletes the ownerless ReplicaSets and, after them, their Pods. `--cascade=orphan` sends `propagationPolicy: Orphan`. The Deployment gets an `orphan` finalizer, the garbage collector strips its ownerReference from each ReplicaSet, and only then does the Deployment go. The ReplicaSet keeps all 7 Pods serving. Orphaning detaches only the direct children, so the Pods still belong to the ReplicaSet. The third value, `--cascade=foreground`, keeps the Deployment visible until its dependents are gone.

code

bash · 3 lines
bash
kubectl get rs -l app=flag-eval -o jsonpath='{.items[*].metadata.ownerReferences[*].name}'
kubectl delete deployment flag-eval --cascade=orphan
kubectl get rs,pods -l app=flag-eval

go deeper

for a junior

Know that the default delete cascades to ReplicaSets and Pods, and that --cascade=orphan leaves them running. Be able to name the three --cascade values.

for a middle

Explain that the link lives in the child's metadata.ownerReferences, that the garbage collector in kube-controller-manager does the deleting, and how the orphan finalizer sequences the detach.

for a senior

Show when orphaning is the right tool, such as recreating an owner whose field cannot be updated, and warn about the unmanaged objects and accidental adoption it can leave behind.

for a principal

Weigh orphan-and-adopt migrations against a blue-green replacement: the first avoids restarts but relies on selector hygiene, so platform guidance should say which one teams use.

## The owner graph behind a Deployment Kubernetes objects rarely live alone. A **Deployment** creates **ReplicaSets**, and each ReplicaSet creates **Pods**. The link between them is written into the child, not the parent: every ReplicaSet carries an entry in `metadata.ownerReferences` that names its Deployment by `apiVersion`, `kind`, `name` and `uid`, and every Pod carries one naming its ReplicaSet. The child is called the **dependent** and the parent the **owner**. The **garbage collector** is a controller that runs inside `kube-controller-manager`. It watches objects across the cluster, builds a graph from these references, and acts when an owner disappears. Because the reference stores the owner's **UID**, the garbage collector cares about the exact object, not just its name. Take the feature-flag evaluation service on a 12-node GPU model-serving cluster: a 7-replica Deployment called `flag-eval`. Its graph is one Deployment, one current ReplicaSet (plus any old ones kept for rollback), and 7 Pods under the current ReplicaSet. ## What `kubectl delete deployment` does by default `kubectl delete` has a `--cascade` flag with three values: `background`, `foreground` and `orphan`. The default is **`background`**, and a bare `--cascade` means the same thing. kubectl sends that choice to the API server as `propagationPolicy: Background` in the delete request. kubectl does not walk the graph or delete the children itself. With **Background** propagation: 1. The API server removes the `flag-eval` Deployment right away. 2. The garbage collector sees that each ReplicaSet now points at an owner that no longer exists, and deletes it. 3. Each ReplicaSet's deletion leaves its Pods pointing at a missing owner, so the garbage collector deletes those Pods too, and they shut down gracefully. So `kubectl get deployment` shows nothing at once, while `kubectl get pods` can still show the 7 Pods in `Terminating` for a few seconds. ## What `--cascade=orphan` changes `--cascade=orphan` sends `propagationPolicy: Orphan`. The sequence is different: 1. The API server sets the Deployment's `metadata.deletionTimestamp` and adds the **`orphan` finalizer** to `metadata.finalizers`. A **finalizer** is a key that must be removed before the object can leave storage. 2. The garbage collector removes the ownerReference that points at `flag-eval` from each **direct** dependent, meaning its ReplicaSets. 3. The garbage collector then removes the `orphan` finalizer, and the Deployment is deleted. The ReplicaSet survives with no owner, and it keeps its 7 Pods running. The Pods are untouched: they still name the ReplicaSet as their owner, because orphaning only detaches the deleted object's direct children. Traffic through a Service that selects those Pods is not interrupted. ## The three values side by side | `--cascade` value | Deployment removed | ReplicaSets and Pods | |---|---|---| | `background` (default) | immediately | deleted afterwards by the garbage collector | | `foreground` | only after blocking dependents are gone | deleted first; the Deployment waits with a `deletionTimestamp` | | `orphan` | after references are stripped | keep running with no owner reference to it | The old boolean spellings still parse but print a deprecation warning: `--cascade=true` maps to `background` and `--cascade=false` maps to `orphan`. ## When to orphan on purpose, and the traps Orphaning is a way to **replace an owner without restarting its workload**: - replacing a controller object when a field you need to change cannot be updated in place, and recreating it so it picks up the existing children; - moving live Pods from one owner definition to another during a migration, with no restart; - a StatefulSet whose `volumeClaimTemplates` must change: delete it with `--cascade=orphan`, recreate it, and the new StatefulSet adopts the running Pods. The traps: - **Orphans are unmanaged by that owner.** Nothing will re-create a Pod the old Deployment owned if the ReplicaSet is also gone, and nothing rolls it out any more. - **Adoption is automatic.** A new controller whose selector matches an ownerless object can claim it by adding its own ownerReference. That is usually the goal, but a careless selector can adopt objects you meant to leave alone. - **Orphans are easy to forget.** A ReplicaSet with no owner holds its GPU requests until someone deletes it, which matters on a 12-node GPU cluster. ## Checking the graph yourself Read the reference before you delete, so you know what will cascade: ```bash kubectl get rs -l app=flag-eval \ -o jsonpath='{range .items[*]}{.metadata.name}{" -> "}{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}{end}' kubectl delete deployment flag-eval --cascade=orphan kubectl get rs,pods -l app=flag-eval ``` After the orphan delete, the ReplicaSet is still listed with its 7 ready Pods, and its `metadata.ownerReferences` field is gone.

  • After an orphan delete, you apply the flag-eval Deployment again. What happens to the orphaned ReplicaSet?
    The Deployment controller looks for ReplicaSets that match its selector and have no controlling owner, and adopts them by adding its own ownerReference. If the pod template is unchanged, it treats the adopted ReplicaSet as the current one and no Pods restart. If the template differs, a normal rolling update replaces the adopted ReplicaSet's Pods.
  • Why would anyone orphan on purpose rather than simply delete and recreate?
    To replace an owner without restarting its workload. The classic case is a StatefulSet whose `volumeClaimTemplates` cannot be updated in place: delete it with `--cascade=orphan`, recreate it with the new template, and the new StatefulSet adopts the running Pods. The same pattern works for any controller object with a field that cannot be updated.
  • What does kubectl do with the old boolean values --cascade=true and --cascade=false?
    It still accepts them but prints a deprecation warning. `--cascade=true` is treated as `background` and `--cascade=false` as `orphan`. Scripts should use the named values, which also make the third mode, `foreground`, available.

Closing a company normally lays off its departments and then their staff. Orphaning dissolves only the parent company: the departments carry on as independent firms with their staff still on the books.

saying these in an interview costs you the question

  • Deleting a Deployment leaves its Pods running until you delete them separately
  • kubectl itself lists and deletes every ReplicaSet and Pod one by one
  • --cascade=orphan also removes each Pod's reference to its ReplicaSet
  • --cascade=false is the current, non-deprecated way to orphan dependents
  • Orphaned Pods are paused until some controller adopts them