KubernetesV · Kubernetes Objects and MetadataKubernetes objects and metadata
Owner references and garbage collection
What you'll learn
- Identify what ownerReferences do and how the garbage collector uses them
- Trace the cascading delete: parent deleted -> children deleted
- Distinguish foreground from background cascading delete
- Apply adoption and orphaning patterns in production
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
ownerReferences are how Kubernetes tracks which object
created which. The garbage collector uses them to cascade
deletes: when a parent is deleted, the children are deleted
too. This lesson covers how owner refs work, how to use them
safely, and the patterns that arise from misconfigured
ownership.
What ownerReferences do
When a controller (or kubectl) creates an object, it can set
metadata.ownerReferences to point at the parent:
apiVersion: v1
kind: Pod
metadata:
name: web-abc
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: web-7c8
uid: 7c8f2d8e-b2e1-4f6a-9c8d-1a2b3c4d5e6f
controller: true
blockOwnerDeletion: true
The garbage collector uses the owner reference graph to cascade deletes:
flowchart LR
D[Deployment deleted] --> RS[ReplicaSet deleted]
RS --> P[Pods deleted]
P --> PVC[PVCs deleted]
PVC --> PV[PVs deleted]
Each step in the cascade uses owner references to determine what to delete.
The fields of ownerReferences
Each entry in ownerReferences has:
apiVersion— the parent’s API versionkind— the parent’s kindname— the parent’s nameuid— the parent’s UID (not the name; UIDs are stable)controller—trueif this is the primary owner (only one controller per object)blockOwnerDeletion—trueif the parent’s deletion should be blocked until this object is deleted
The uid is what the GC matches on. The name is for
human-readable identification; if name differs from the
referenced object’s name, the GC ignores the reference.
How cascading delete works
When an object with owned children is deleted:
- The object’s
metadata.deletionTimestampis set. - The object’s finalizers are processed (see kubernetes-v-06).
- After finalizers are removed, the object is deleted from etcd.
- The garbage collector sees the parent is gone.
- For each child with
ownerReferences[].uid == parent.uid, the GC deletes the child (recursively).
sequenceDiagram
autonumber
participant U as Operator
participant API as API server
participant GC as Garbage collector
participant ETCD as etcd
U->>API: DELETE /deployments/web
API->>API: set metadata.deletionTimestamp
Note over API: finalizers processed
API->>ETCD: persist (deletionTimestamp set)
API->>ETCD: delete from etcd
GC->>ETCD: list children of web (owner.uid match)
GC->>ETCD: delete each child
Note over GC: cascades to grandchildren
Foreground vs background cascading
There are two propagation policies:
Background (default)
The parent is deleted immediately; children are deleted in the background by the GC.
kubectl delete deployment web --cascade=background # default
Production: this is the fastest and most common. The parent returns “deleted” while children are still being reaped.
Foreground
The parent’s deletion is blocked until all children are deleted first. The parent remains “deletion in progress” until the cascade completes.
kubectl delete deployment web --cascade=foreground
Production: this is safer for ordered deletion. The parent does not return until all children are gone.
Orphan
The parent is deleted; children are not deleted; they become orphans (no owner).
kubectl delete deployment web --cascade=orphan
Production: useful for promoting children to standalone objects (e.g., preserving a PVC when deleting a StatefulSet).
Adoption: adding an owner ref to an existing object
Sometimes a controller wants to “adopt” an existing object — add an owner reference to an object the controller didn’t create.
err := controllerutil.SetOwnerReference(parent, child, scheme)
The API server validates adoption:
- The new owner reference must have a different
uidthan the existing references (if any) - The actor must have permission to update the object
- If the object has multiple potential owners, adoption requires a merge strategy
In production, adoption is rare. The common case: a manual object (e.g., a ConfigMap) is adopted by a new controller that takes over.
Multiple owners
An object can have multiple owner references:
metadata:
ownerReferences:
- apiVersion: apps/v1
kind: Deployment
name: web
uid: ...
- apiVersion: v1
kind: Service
name: web
uid: ...
The GC deletes the object if any owner is deleted (and the object is not protected). This is rarely what you want.
Production rule: one owner per object. Multi-ownership creates ambiguity and surprises during deletion.
blockOwnerDeletion
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: web-7c8
uid: ...
blockOwnerDeletion: true
With blockOwnerDeletion: true, the parent’s deletion is
blocked until this child is deleted. This is set when
the child is critical to the parent’s function.
The Deployment controller sets blockOwnerDeletion: true on
its owned ReplicaSets. So when you kubectl delete deployment web:
- The Deployment is marked for deletion.
- The Deployment controller sees the deletion and tries to delete the owned ReplicaSets.
- The ReplicaSet has
blockOwnerDeletion: trueon its owner ref to the Deployment. This is fine — the block applies to deleting the Deployment, not deleting the ReplicaSet. - The ReplicaSet is deleted.
- The ReplicaSet’s Pods are deleted (with their own owner refs to the ReplicaSet).
But: if you try to delete a ReplicaSet that has Pods with
blockOwnerDeletion: true referencing it, the API server
will block the deletion until the Pods are deleted first.
This is mostly automatic; operators rarely interact with
blockOwnerDeletion directly.
Adoption patterns
Pattern 1: Helm adoption
Helm 3 sets owner references on every object it creates. The
Helm release tracks ownership; helm uninstall cascades.
helm install web ./chart
# creates Deployment, Service, ConfigMap — all owned by Helm release
helm uninstall web
# deletes all of them via owner references
Pattern 2: ArgoCD / Flux
GitOps controllers set owner references on managed objects.
kubectl delete of an ArgoCD-managed object is reverted by
ArgoCD on the next sync.
Pattern 3: manual ownership assignment
kubectl patch pod web-abc -p '
metadata:
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: web-7c8
uid: 7c8f2d8e-b2e1-4f6a-9c8d-1a2b3c4d5e6f
controller: true
'
Rare in production. The typical case: a tool that adopts existing objects.
Production discipline
- Always set
ownerReferenceswhen creating objects via automation — controllers, GitOps, Helm. Without them the cascading delete has no graph to walk and the children are left orphaned. - Avoid multiple owners; pick one canonical owner per object.
- Use
--cascade=orphanwhen promoting children to standalone objects — a StatefulSet recreated with a new image is the common case, because the default cascade takes the PVCs with it. - Audit
ownerReferencesin incident response: who owns this Pod, and what will be deleted if the owner goes? - Test deletion cascades in staging before applying in production.
Cross-course references
- The Linux course part
XXIX-Linux-Hardeningcovers least-privilege principles that map onto ownership discipline. - The Observability course part
CIX-Observability-InvestigationWorkflowscovers investigation methodology that benefits from understanding owner graphs. - The Linux course part
XXIV-Linux-Timecovers chrony — timestamps in deletion depend on clock accuracy. - The Docker course part
XXXVIII-Docker-Secretscovers secret handling; ownership is not for secrets.
Quiz
Knowledge check · 4 questions
Q1. What does the garbage collector use to identify a parent in an ownerReference?
Q2. An object can have multiple ownerReferences, and the GC will keep the object as long as at least one owner exists.
Q3. A team needs to recreate a StatefulSet with a new image while preserving the PVCs. The current StatefulSet has `replicas: 3`, three Pods, and three PVCs backed by a StorageClass with `reclaimPolicy: Delete`. Walk through the procedure that preserves the PVCs.
StatefulSet: ```yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: db spec: replicas: 3 serviceName: db template: spec: containers: - name: postgres image: postgres:14 volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 100Gi ``` PVCs: ``` $ kubectl get pvc -l app=db NAME STATUS VOLUME CAPACITY data-db-0 Bound pv-001 100Gi data-db-1 Bound pv-002 100Gi data-db-2 Bound pv-003 100Gi ``` Goal: recreate the StatefulSet with `postgres:16` image, preserving the data in the PVCs.
Q4. Explain `--cascade=orphan` and when to use it. Why is it useful for StatefulSet upgrades but not for routine Deployment deletes?
Passing score: 75%. Answers are checked in this browser.