Skip to main content
RunBook Academy

KubernetesV · Kubernetes Objects and MetadataKubernetes objects and metadata

Owner references and garbage collection

Intermediate⏱ ~14 minkubectl

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

Not yet marked complete on this device.

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 version
  • kind — the parent’s kind
  • name — the parent’s name
  • uid — the parent’s UID (not the name; UIDs are stable)
  • controllertrue if this is the primary owner (only one controller per object)
  • blockOwnerDeletiontrue if 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:

  1. The object’s metadata.deletionTimestamp is set.
  2. The object’s finalizers are processed (see kubernetes-v-06).
  3. After finalizers are removed, the object is deleted from etcd.
  4. The garbage collector sees the parent is gone.
  5. 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 uid than 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:

  1. The Deployment is marked for deletion.
  2. The Deployment controller sees the deletion and tries to delete the owned ReplicaSets.
  3. The ReplicaSet has blockOwnerDeletion: true on its owner ref to the Deployment. This is fine — the block applies to deleting the Deployment, not deleting the ReplicaSet.
  4. The ReplicaSet is deleted.
  5. 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 ownerReferences when 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=orphan when 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 ownerReferences in 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-Hardening covers least-privilege principles that map onto ownership discipline.
  • The Observability course part CIX-Observability-InvestigationWorkflows covers investigation methodology that benefits from understanding owner graphs.
  • The Linux course part XXIV-Linux-Time covers chrony — timestamps in deletion depend on clock accuracy.
  • The Docker course part XXXVIII-Docker-Secrets covers secret handling; ownership is not for secrets.

Quiz

Knowledge check · 4 questions

  1. Q1. What does the garbage collector use to identify a parent in an ownerReference?

  2. Q2. An object can have multiple ownerReferences, and the GC will keep the object as long as at least one owner exists.

  3. 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.

  4. 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.