KubernetesIV · Desired State and ReconciliationDesired state and reconciliation
Built-in controllers — what each one does
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
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 onstatus.replicas— total Pods (active + previous)status.updatedReplicas— Pods at the latest templatestatus.readyReplicas— Pods readystatus.availableReplicas— Pods available (ready for minReadySeconds)status.conditions—Progressing,Available,ReplicaFailure
Failure modes:
- Stuck at
Progressing: False, ReplicaFailure: Trueif the Pod template is invalid - Available condition false if
minReadySecondsnot 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
volumeClaimTemplatesfor each Pod - Scales up sequentially; scales down in reverse order
- Updates Pods in order (or all at once for
OrderedReadyvsParallel)
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 fineForbid— skip new run if previous still runningReplace— cancel previous, start new
Status fields: status.lastScheduleTime,
status.lastSuccessfulTime, status.active (active Jobs).
Failure modes:
- Schedule missed (
startingDeadlineSecondsexceeded) - 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 namespaceblocked 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 kubectlkube-controller-manager— set by a built-in controllerdeployment-controller— specifically by the Deployment controllerendpointslice-controller— by the EndpointSlice controller...
server-side apply records managed fields; imperative
kubectl does not.
Cross-course references
- The Linux course part
VI-Linux-Processescovers the process management primitives controllers run on top of. - The Observability course part
IX-Observability-Exporterscovers the metrics surface that exposes controller health. - The Linux course part
XXIV-Linux-Timecovers chrony — Node lease renewal depends on clocks within tolerance. - The Docker course part
XXX-Docker-Lifecyclecovers the lifecycle primitives that workload controllers drive.
Quiz
Knowledge check · 4 questions
Q1. Which controller updates the `status.replicas` field of a Deployment?
Q2. When a Deployment is deleted, the garbage collector deletes the owned ReplicaSets and Pods because the owner reference graph connects them.
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 ```
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.
managedFieldsis the source of truth. - Audit owner references on every object. Orphans are a resource leak and a source of confusing cluster state.
- Monitor
status.conditionsof major objects (Deployments, StatefulSets, Nodes). A condition stuckFalseis 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".