skip to content

How do Kubernetes ownerReferences, with their controller and blockOwnerDeletion fields, drive the Foreground, Background and Orphan deletion propagationPolicy values?

level: middleimportance: should knowfreq 48%

answer

  1. the reference lives on the child
  2. matched by UID, not by name
  3. at most one managing owner
  4. blocking only counts in foreground
  5. two finalizers: foregroundDeletion and orphan

basics

~20 s

An ownerReference names an owner by kind, name and UID, and the garbage collector deletes a dependent once all its owners are gone. Background deletes the owner first, Foreground waits for blocking dependents, and Orphan detaches them.

solid answer

~40 s

Each dependent lists its owners in `metadata.ownerReferences` by `apiVersion`, `kind`, `name` and `uid`, and the garbage collector in `kube-controller-manager` deletes it once all of those owners are gone. `controller: true` marks the one managing owner, which controllers use for adoption. `blockOwnerDeletion: true` matters only in foreground deletion. `propagationPolicy` on the delete request sets the order. With `Background`, the owner is deleted at once and the dependents follow. With `Foreground`, the owner gets a `deletionTimestamp` and the `foregroundDeletion` finalizer, and stays until every blocking dependent is gone. The cascade continues down the tree. With `Orphan`, the owner gets the `orphan` finalizer, its references are removed from its direct dependents, and then it goes. Owners must be in the same namespace as the dependent, or be cluster-scoped.

code

yaml · 16 lines
yaml
apiVersion: v1
kind: Pod
metadata:
  name: flag-eval-6d9c7b5f4-x2k8q
  namespace: flags
  ownerReferences:
  - apiVersion: apps/v1
    kind: ReplicaSet
    name: flag-eval-6d9c7b5f4
    uid: 3f1c2a9e-7b54-4d61-9a0e-5c8d2e71b604
    controller: true
    blockOwnerDeletion: true
spec:
  containers:
  - name: evaluator
    image: registry.example.com/flag-eval:2.14.3

go deeper

for a junior

Remember that the child stores the reference to its owner, and that there are three policies: Background, Foreground and Orphan.

for a middle

Walk through each policy's finalizer and ordering, explain that references match by UID, and say that blockOwnerDeletion only matters for Foreground.

for a senior

Call out the traps you have seen: Jobs orphaning Pods when a client omits the policy, dangling references after name reuse, and cross-namespace references that get collected.

for a principal

Decide which propagation policy platform tooling sends by default, and whether to enable OwnerReferencesPermissionEnforcement so tenants cannot block each other's deletions.

## Owners, dependents and the reference itself In Kubernetes an object that another object created or manages is a **dependent**, and the object responsible for it is its **owner**. The link is stored on the dependent, in `metadata.ownerReferences`, a list of entries with these fields: - `apiVersion`, `kind`, `name` and `uid` identify the owner. The **UID** is what counts. If an owner is deleted and a new object is created with the same name, the old reference does not point at it. - `controller` marks the one owner that **manages** the dependent. At most one entry may set it to `true`, and the API server rejects a second one. Controllers use this flag to decide which objects are theirs and whether an ownerless object can be adopted. - `blockOwnerDeletion` says whether this dependent must be deleted before its owner can leave storage **during foreground deletion**. It does nothing in the other modes. The owner and the dependent must live in the same namespace, or the owner must be cluster-scoped. There is no namespace field in the reference. A cluster-scoped dependent may name only cluster-scoped owners. When the garbage collector finds a namespaced dependent that points at a namespaced kind it cannot find in the dependent's own namespace, it records a `Warning` event with reason `OwnerRefInvalidNamespace`. Built-in controllers create references with `controller: true` and `blockOwnerDeletion: true`. A ReplicaSet does this for its Pods, and a Deployment for its ReplicaSets. ## How the garbage collector decides The **garbage collector** runs in `kube-controller-manager`. It watches every deletable resource and keeps a graph of owners and dependents. Its core rule: a dependent is deleted once **all** of its owners are gone. If a Pod lists two owners and one is deleted, the garbage collector removes the dangling reference and keeps the Pod. The `controller` flag does not change this rule; every listed owner counts. When a delete request arrives, `propagationPolicy` in the `DeleteOptions` body decides the order. The older `orphanDependents` boolean is deprecated, and a request may set it or `propagationPolicy`, not both. ## The three propagation policies | `propagationPolicy` | What the API server does | What the garbage collector does | When the owner leaves storage | |---|---|---|---| | `Background` | deletes the owner right away | deletes dependents afterwards | first | | `Foreground` | sets `deletionTimestamp`, adds the `foregroundDeletion` finalizer | deletes dependents, again with Foreground if they have dependents of their own | after every `blockOwnerDeletion: true` dependent is gone | | `Orphan` | sets `deletionTimestamp`, adds the `orphan` finalizer | removes the reference to this owner from each direct dependent | after the references are stripped | **Foreground** cascades down the tree. For the 7-replica feature-flag evaluation Deployment on the 12-node GPU cluster, the sequence is: 1. The Deployment gets its `deletionTimestamp` and the `foregroundDeletion` finalizer, and stays readable. 2. The garbage collector deletes the ReplicaSet with Foreground, so the ReplicaSet also waits. 3. The 7 Pods are deleted and shut down gracefully. Each has `blockOwnerDeletion: true`, so the ReplicaSet waits for all of them. 4. The ReplicaSet's finalizer is removed and it disappears. The Deployment's finalizer is then removed, and it disappears last. While the ReplicaSet carries a `deletionTimestamp`, its controller stops creating replacement Pods, so the deletion is not undone by a reconcile. ## Who may set `blockOwnerDeletion` Setting `blockOwnerDeletion: true` lets a client delay someone else's deletion. The **`OwnerReferencesPermissionEnforcement`** admission plugin guards this: when enabled, it rejects a request that sets the flag unless the user may update the owner's `finalizers` subresource. The plugin is **off by default** in kube-apiserver, so check whether your cluster enables it before relying on it. ## Per-resource defaults and client traps When a request omits `propagationPolicy`, the resource's own default applies: - Most resources, including Deployments and ReplicaSets, default to deleting dependents in the background. - `batch/v1` **Jobs default to orphaning** their Pods, kept for backward compatibility. The API server returns a warning that child Pods are preserved and suggests `propagationPolicy=Background`. - `kubectl delete` always sends an explicit policy, `Background` unless told otherwise, so kubectl users do not hit this. Code written against client libraries that passes empty delete options does. A delete request with the policy set explicitly: ```json { "apiVersion": "v1", "kind": "DeleteOptions", "propagationPolicy": "Foreground" } ``` ## Reasoning checks - **Background** is the right default for most cleanup: fast and asynchronous. - **Foreground** is for callers that must not proceed until the children are really gone, such as a pipeline that re-creates an object with the same name. - **Orphan** is for replacing an owner while keeping its children alive. - A missing UID match counts as a missing owner, so hand-written references must copy the live UID.

  • Code that deletes a Kubernetes Job through a client library leaves the Job's Pods behind, but kubectl delete job does not. Why?
    Resources have their own default when a request omits `propagationPolicy`, and `batch/v1` Jobs default to orphaning their Pods for backward compatibility. The API server even returns a warning saying so. kubectl always sends `Background`, so it cleans up. A client library call with empty delete options gets the orphan default. The fix is to set `propagationPolicy: Background` explicitly.
  • You delete an owner and immediately create a new one with the same name. Do the old dependents attach to the new owner?
    Not through their references. An ownerReference matches on `uid`, so the old references are now dangling and the garbage collector will delete those dependents. A controller may adopt a matching object that has no controlling owner, but that is a separate step. Relying on name reuse to keep children is a race.
  • What happens if a namespaced dependent lists a namespaced owner that lives in another namespace?
    The reference has no namespace field, so it is always resolved in the dependent's own namespace. The owner is not found there, so the dependent is treated as having a missing owner and can be garbage collected. The garbage collector also records a `Warning` event with reason `OwnerRefInvalidNamespace` on the dependent.

saying these in an interview costs you the question

  • The garbage collector only follows references that set controller: true
  • blockOwnerDeletion holds up the owner in every propagation mode
  • Background deletion waits for dependents before removing the owner
  • An ownerReference can point at a namespaced owner in another namespace
  • Re-creating an owner with the same name re-attaches its old dependents
  • Every resource defaults to background propagation over the raw API