Skip to main content
RunBook Academy

Git, CI/CD & GitOpsFinal · Final AssessmentFinal Review

GitOps reconciliation and drift — recap

Advanced⏱ ~28 mingit

What you'll learn

  • State the four GitOps principles and identify which one a given violation breaks
  • Trace the reconciliation loop from observed state through diff to apply, for both Argo CD and Flux
  • Distinguish the three drift types (manual, accidental, emergency) and match each to a control
  • Read the Argo CD and Flux status fields (Synced/Healthy, Ready/Ready) to diagnose a controller failure
  • Decide when self-heal helps, when prune is safe, and when a break-glass is the only correct response

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

GitOps is not a product; it is a set of four properties that any system can implement: the desired state is declarative, the desired state is versioned in Git, the controllers pull the desired state, and reconciliation runs continuously. Get any one of the four wrong and the system is no longer GitOps — it is “configuration in Git” with some pieces missing.

The four principles and what breaks when each one is violated

flowchart TD
    A["Desired state"] -->|declarative| B["YAML, manifests, Helm chart"]
    B -->|versioned| C["Git commit, signed, immutable ref"]
    C -->|pulled| D["Controller reads Git"]
    D -->|reconciled| E["Loop: observed = desired"]
    style A fill:#dff
    style B fill:#dff
    style C fill:#dff
    style D fill:#dff
  • Declarative. The desired state is expressed as data, not procedure. A Deployment manifest is declarative; a bash script that creates a Deployment is not. Violation: imperative operators that produce state without leaving a record.
  • Versioned. The desired state lives in Git, with every change a commit and a pull request. Violation: manifests edited in place via kubectl edit or applied from a laptop.
  • Pulled. The controller in the cluster fetches the desired state from Git; the deploy system does not push manifests into the cluster’s API. Violation: CI systems that kubectl apply against the cluster API.
  • Continuously reconciled. The controller runs the diff-and-apply loop on a schedule (or on Git webhook) for as long as the application exists. Violation: one-shot apply pipelines that do not retry.

The reconciliation loop

The loop, in five steps, is identical in Argo CD and Flux:

  1. Fetch. The controller reads the desired state from Git (Argo CD: from the configured repo URL; Flux: via the GitRepository and Kustomization CRDs).
  2. Observe. The controller reads the observed state from the cluster (via the Kubernetes API).
  3. Diff. The controller computes the difference between desired and observed.
  4. Apply. The controller applies the diff to the cluster (Argo CD: server-side apply; Flux: server-side apply with owner annotations).
  5. Report. The controller updates the status (Application.Status in Argo CD, Kustomization.Status in Flux) to reflect whether the observed state now matches the desired state.
flowchart LR
    A[Fetch] --> B[Observe]
    B --> C[Diff]
    C -->|"desired != observed"| D[Apply]
    D --> B
    C -->|"desired = observed"| E[Healthy]

The synced and healthy matrix

A controller’s status is two orthogonal fields, and a failure in one is not a failure in the other:

  • Synced / NotSynced. Does the observed state in the cluster match the desired state in Git? A NotSynced status means a sync is pending or has failed.
  • Healthy / Degraded. Are the resources in the desired state actually working? A Degraded status means the resources are applied but failing (a CrashLoopBackOff, an ImagePullBackOff, a failed readiness probe).

The four combinations map to four operational states:

SyncedHealthyMeaning
SyncedHealthySteady state. The loop is happy.
SyncedDegradedThe manifests are applied but the workload is failing. Investigate the workload, not the controller.
NotSyncedHealthyA new commit is pending or a sync is in progress. Wait, or check the controller logs.
NotSyncedDegradedEither the sync failed (manifest error) or the workload was failing before the new commit. Read both layers.

Drift — the three types and the matching controls

Drift is any change to the observed state that did not come through the reconciliation loop:

  • Manual drift. Someone ran kubectl edit, kubectl scale, or kubectl patch to change a resource directly. The next reconciliation will revert the change if self-heal is enabled; otherwise it persists.
  • Accidental drift. A node failure, a network partition, or a controller crash left the cluster in a partial state. Self-heal converges back to the desired state on the next reconciliation.
  • Emergency drift. A break-glass change made during an incident, applied via kubectl apply or a hot-patch. The change is real and may need to stay; the after-incident work is to reconcile Git to match the cluster, or to revert the cluster to match Git.

Production discipline

The five rules that recur across every GitOps deployment:

  1. The controller is the only writer. Any change to a managed resource that does not go through the controller is drift. The cluster’s API is reached by the controller and by emergency break-glass procedures, full stop.
  2. Self-heal on, prune off by default. Self-heal converges drift; prune removes orphans. The first is a guardrail; the second is a footgun.
  3. Break-glass is logged. A break-glass change bypasses Git; the after-incident work is to put the change back into Git so the audit trail survives.
  4. kubectl edit is not a workflow. If a change is worth making in the cluster, it is worth committing in Git so the next reconciliation does not undo it.
  5. The status fields are the dashboard. Synced and Healthy are not metrics; they are the controller telling you whether the cluster matches what you said it should match. An Application that is NotSynced for an hour is an Application whose desired state has drifted from its observed state.

Cross-course references

  • Kubernetes for Production Sysadmins — Controllers and reconciliation loops are the core pattern of Kubernetes itself; the GitOps controller is a specialisation of the same loop.
  • Terraform for Production Sysadmins — Terraform apply is a one-shot reconciliation; Terraform drift detection is the same question as Argo CD’s drift detection, with a different cadence.

Quiz

Knowledge check · 4 questions

  1. Q1. An `Application` in Argo CD shows `Status: Synced` and `Health: Degraded`. What is the most likely cause?

  2. Q2. Self-heal and prune are independent controls: self-heal converges a managed resource back to its declared state, while prune deletes resources that are no longer declared in Git.

  3. Q3. Name the four GitOps principles and the one non-obvious reason the controller pulls the desired state from Git rather than having a CI system push manifests to the cluster.

  4. Q4. During an incident, an on-call engineer runs `kubectl scale deployment api --replicas=10` to handle a load spike. The change works and the incident is resolved. The next morning, the application is back to 3 replicas and the engineer is confused. Walk through what happened, why, and what should have been done instead.

    Production is running with an Argo CD `Application` for the `api` service. Self-heal is enabled; prune is disabled. The `Deployment` manifest in Git declares `replicas: 3`. During the incident, the engineer ran `kubectl scale deployment api --replicas=10` directly against the cluster. Argo CD's reconciliation interval is 3 minutes. The next morning, the engineer sees `replicas: 3` again.

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