Skip to main content
RunBook Academy

KubernetesIV · Desired State and ReconciliationDesired state and reconciliation

Built-in controllers — what each one does

Advanced⏱ ~18 minkubectl

What you'll learn

  • Identify the controllers built into kube-controller-manager and what each reconciles
  • Trace a Deployment through Deployment, ReplicaSet, and Pod controllers and their relationships
  • Explain the garbage collector and how owner references drive cascading delete
  • Identify which controller owns which status fields

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.

kube-controller-manager runs dozens of controllers. This lesson walks each major one — what it reconciles, its key status fields, and how it coordinates with the others.

The controller catalogue

flowchart TB
    subgraph Workloads
        D[Deployment]
        R[ReplicaSet]
        ST[StatefulSet]
        DM[DaemonSet]
        J[Job]
        CJ[CronJob]
    end
    subgraph Services
        S[Service]
        EP[EndpointSlice]
    end
    subgraph Cluster
        N[Node]
        NS[Namespace]
        SA[ServiceAccount]
    end
    subgraph Storage
        PV[PersistentVolume]
    end
    subgraph Policy
        RQ[ResourceQuota]
    end
    subgraph Crosscutting
        GC[Garbage collector]
    end

Each controller is a goroutine inside the kube-controller-manager process. They share the API server client and the leader election lease.

Workload controllers

Deployment controller

Reconciles Deployment objects.

Spec inputs: replicas, selector, template, strategy.

Reconcile action:

  • Maintains 1-2 ReplicaSets (active + previous during rollout)
  • Scales the active ReplicaSet to spec.replicas
  • On Pod template change: creates a new ReplicaSet, scales the old one down according to strategy.rollingUpdate

Status fields:

  • status.observedGeneration — last generation the controller acted on
  • status.replicas — total Pods (active + previous)
  • status.updatedReplicas — Pods at the latest template
  • status.readyReplicas — Pods ready
  • status.availableReplicas — Pods available (ready for minReadySeconds)
  • status.conditionsProgressing, Available, ReplicaFailure

Failure modes:

  • Stuck at Progressing: False, ReplicaFailure: True if the Pod template is invalid
  • Available condition false if minReadySeconds not met
  • ReplicaSet generation drift if rollout paused for too long

ReplicaSet controller

Reconciles ReplicaSet objects.

Spec inputs: replicas, selector, template.

Reconcile action: Creates/deletes Pods to match spec.replicas.

Status fields: status.replicas, status.readyReplicas, status.fullyLabeledReplicas.

Failure modes: Pod stuck Pending (downstream of scheduler); selector mismatch (Pods created but not selected).

StatefulSet controller

Reconciles StatefulSet objects.

Spec inputs: replicas, selector, template, volumeClaimTemplates, serviceName, podManagementPolicy.

Reconcile action:

  • Creates Pods in order (0, 1, 2, …) with stable identity
  • Creates PVCs from volumeClaimTemplates for each Pod
  • Scales up sequentially; scales down in reverse order
  • Updates Pods in order (or all at once for OrderedReady vs Parallel)

Status fields: status.replicas, status.readyReplicas, status.currentReplicas, status.updatedReplicas, status.conditions.

Failure modes:

  • Pod 0 not Ready blocks Pod 1 creation (ordered)
  • Stuck Pod deletion (waiting for PVC unmount)
  • Volume claim template changes require manual intervention

DaemonSet controller

Reconciles DaemonSet objects.

Spec inputs: selector, template, updateStrategy, nodeSelector, tolerations.

Reconcile action:

  • Maintains one Pod per matching node
  • Adds Pods to new nodes automatically
  • Removes Pods from cordoned / deleted nodes
  • Rolls updates according to updateStrategy

Status fields: status.desiredNumberScheduled, status.currentNumberScheduled, status.numberReady, status.numberAvailable, status.conditions.

Failure modes:

  • DaemonSet missing on a node (nodeSelector/toleration mismatch)
  • Rollout blocked (maxUnavailable=0, no Pod can be unavailable)

Job controller

Reconciles Job objects.

Spec inputs: completions, parallelism, backoffLimit, activeDeadlineSeconds, template.

Reconcile action: Creates Pods to achieve completions; retries up to backoffLimit; fails after activeDeadlineSeconds.

Status fields: status.active, status.succeeded, status.failed, status.completionTime, status.conditions.

Failure modes:

  • Job hangs (Pods crashlooping without success)
  • Job exceeds activeDeadlineSeconds
  • Pod template not running due to image/scheduling issues

CronJob controller

Reconciles CronJob objects.

Spec inputs: schedule, concurrencyPolicy, startingDeadlineSeconds, successfulJobsHistoryLimit, failedJobsHistoryLimit, jobTemplate.

Reconcile action: Creates Jobs on the cron schedule. concurrencyPolicy:

  • Allow — overlap is fine
  • Forbid — skip new run if previous still running
  • Replace — cancel previous, start new

Status fields: status.lastScheduleTime, status.lastSuccessfulTime, status.active (active Jobs).

Failure modes:

  • Schedule missed (startingDeadlineSeconds exceeded)
  • CronJob paused or suspended without notice

Service controllers

Service controller

Reconciles Service objects.

Spec inputs: selector, ports, type (ClusterIP, NodePort, LoadBalancer, ExternalName), loadBalancerSourceRanges.

Reconcile action: For LoadBalancer, triggers the cloud controller manager to provision the LB. Tracks status.loadBalancer.ingress.

Status fields: status.loadBalancer.ingress (LB addresses).

Failure modes:

  • Cloud LB provisioning fails (quota, IAM, subnet)
  • LB never gets an IP (provider outage)

EndpointSlice controller

Reconciles EndpointSlice objects.

Spec inputs: none (controller-derived from Service selectors).

Reconcile action: For each Service, lists Pods matching the selector and updates the corresponding EndpointSlices (typically 100 endpoints per slice).

Status fields: endpoints[] with addresses and conditions.

Failure modes:

  • EndpointSlice stale (Pods exist but not in slice)
  • Selector mismatch (no endpoints)
  • Slice count grew beyond default

Cluster controllers

Node controller

Reconciles Node objects.

Spec inputs: none (controller observes node heartbeats).

Reconcile action:

  • Watches kubelet lease renewals
  • Marks nodes NotReady after lease expiry
  • Evicts Pods from NotReady nodes after grace period
  • Garbage-collects unreachable nodes

Status fields: status.conditions[] (Ready, MemoryPressure, DiskPressure, PIDPressure), status.addresses.

Failure modes:

  • Node marked NotReady incorrectly (clock skew, network blip)
  • Pods not evicted (RBAC prevents node controller from listing Pods)

Namespace controller

Reconciles Namespace objects.

Spec inputs: none (controller observes deletion timestamps).

Reconcile action:

  • Maintains status.phase (Active, Terminating)
  • Cleans up resources in a Namespace during deletion
  • Manages deletion timestamp and finalizers

Status fields: status.phase, status.conditions[].reason.

Failure modes:

  • Namespace stuck in Terminating (finalizer not removed)
  • kubectl delete namespace blocked by finalizer

ServiceAccount controller

Reconciles ServiceAccount objects.

Spec inputs: secrets[], imagePullSecrets[], automountServiceAccountToken.

Reconcile action:

  • Creates default ServiceAccount in every new Namespace
  • Manages the legacy token Secret (deprecated)
  • Issues tokens via TokenRequest API for projection

Status fields: secrets[] (legacy, deprecated).

Failure modes:

  • Default SA missing (token not issued)
  • Legacy token Secret not cleaned up

PersistentVolume controller

Reconciles PersistentVolume and PersistentVolumeClaim objects.

Spec inputs: For PV: capacity, accessModes, persistentVolumeReclaimPolicy, storageClassName. For PVC: resources.requests.storage, accessModes, storageClassName.

Reconcile action:

  • Binds PVs to PVCs (static provisioning)
  • Triggers CSI provisioning (dynamic)
  • Recycles/Retains/Deletes PVs on PVC deletion
  • Updates PV phase (Available, Bound, Released, Failed)

Status fields: PV status.phase; PVC status.phase, status.accessModes.

Failure modes:

  • PVC Pending (no PV matching, CSI provision failure)
  • PV stuck Released (reclaim policy misconfigured)
  • PV stuck Failed (CSI error)

ResourceQuota controller

Reconciles ResourceQuota objects.

Spec inputs: spec.hard (the limits).

Reconcile action:

  • Counts resource usage in the namespace
  • Rejects Pods that would exceed the quota
  • Updates status.used

Status fields: status.hard, status.used.

Failure modes:

  • Quota exceeded (rejects every new Pod)
  • Status.used stale (controller lagging)

Crosscutting controllers

Garbage collector

Not tied to a single resource; walks the owner reference graph.

Reconcile action:

  • Cascading delete: when an owner is gone, delete its owned objects
  • Finalizer enforcement: wait for finalizers before deletion

Failure modes:

  • Finalizer prevents deletion indefinitely
  • Owner reference missing (orphaned objects)

TTL controller (1.12+)

Reconciles objects with a TTL annotation: ttl.controller.kubernetes.io/ttl-after-finished.

Reconcile action: Deletes Jobs/Pods after a configured time post-finish.

Node lifecycle controller (1.20+)

Handles graceful node shutdown (SIGTERM to Pods, then --node-shutdown-grace-period, then SIGKILL).

How to identify which controller owns a status field

# Get the field's manager
kubectl get pod web-abc -o json | jq '.metadata.managedFields'

managedFields records who last set each field. The manager field is the controller or kubectl that wrote it:

  • kubectl — set by the operator’s kubectl
  • kube-controller-manager — set by a built-in controller
  • deployment-controller — specifically by the Deployment controller
  • endpointslice-controller — by the EndpointSlice controller
  • ...

server-side apply records managed fields; imperative kubectl does not.

Cross-course references

  • The Linux course part VI-Linux-Processes covers the process management primitives controllers run on top of.
  • The Observability course part IX-Observability-Exporters covers the metrics surface that exposes controller health.
  • The Linux course part XXIV-Linux-Time covers chrony — Node lease renewal depends on clocks within tolerance.
  • The Docker course part XXX-Docker-Lifecycle covers the lifecycle primitives that workload controllers drive.

Quiz

Knowledge check · 4 questions

  1. Q1. Which controller updates the `status.replicas` field of a Deployment?

  2. Q2. When a Deployment is deleted, the garbage collector deletes the owned ReplicaSets and Pods because the owner reference graph connects them.

  3. Q3. A StatefulSet with `replicas: 3` and `podManagementPolicy: OrderedReady` is stuck. Pod 0 is Ready; Pod 1 is Pending (PVC not bound); Pod 2 is not created. The operator wants to know: which controller is responsible for creating Pod 2, and why is it blocked?

    StatefulSet: ```yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: db spec: replicas: 3 podManagementPolicy: OrderedReady serviceName: db template: spec: containers: - name: postgres image: postgres:16 volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 100Gi ``` Pods: ``` $ kubectl get pods -l app=db NAME READY STATUS RESTARTS AGE db-0 1/1 Running 0 2h db-1 0/1 Pending 0 30m db-2 0/1 Pending 0 5m ``` Events on db-1: ``` FailedScheduling Warning no nodes available due to persistentvolumeclaim "data-db-1" not bound ``` PVCs: ``` $ kubectl get pvc -l app=db NAME STATUS VOLUME CAPACITY data-db-0 Bound pv-001 100Gi data-db-1 Pending data-db-2 Pending ```

  4. Q4. Explain owner references and cascading delete. Why is the absence of an owner reference a production anti-pattern?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Identify which controller owns each status field before troubleshooting. managedFields is the source of truth.
  • Audit owner references on every object. Orphans are a resource leak and a source of confusing cluster state.
  • Monitor status.conditions of major objects (Deployments, StatefulSets, Nodes). A condition stuck False is the controller telling you it cannot converge.
  • Understand the dependencies between controllers: a downstream failure (PVC Pending) blocks an upstream reconcile (StatefulSet).
  • Add alerts on controller-specific metrics: workqueue_depth, controller_runtime_seconds, status.conditions[].status == "False".