Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXXVI · GitOps Failure ModesDetection

Unhealthy deployment — synced but broken

Advanced⏱ ~25 mingit

What you'll learn

  • Distinguish Kubernetes resource health from application runtime health
  • Read the Argo CD health status fields that surface a broken application inside a Synced resource set
  • Apply the rollout discipline that catches unhealthy deployments before they reach production
  • Document the SLO that covers application health, not just sync health

Prerequisites

Practice

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.

The fifth failure mode exposes the boundary of what the GitOps controller actually checks. A controller can report Synced and still report Degraded, and a controller can report Synced and Healthy while the application inside the cluster is failing. The reconciliation loop converges the desired state of Kubernetes resources; the health of the application running inside those resources is a separate question, and the controller only answers it if the resource definitions include probes or health checks.

The two dimensions of status

Argo CD and Flux both track two orthogonal dimensions: sync status (does the cluster match the desired state?) and health status (is the application inside the cluster healthy?). A resource can be Synced and Degraded (the controller applied the manifest but the resource itself is failing) or Synced and Healthy (applied and running).

argocd app get guestbook

The output shows both. A broken application looks like:

Sync Status:      Synced
Health Status:    Degraded

with the Conditions block carrying messages like:

Deployment.apps/guestbook: observed generation does not match spec generation
Service: waiting for endpoint
flowchart LR
    A[Desired state in Git] --> B[Controller apply]
    B --> C[Resource in cluster]
    C --> D{Resource healthy?}
    D -- yes --> E["Synced + Healthy"]
    D -- no --> F["Synced + Degraded"]
    F --> G[Per-resource health message]

The gap between Kubernetes reconciliation and application health

The Kubernetes Deployment controller creates a Deployment with the configured number of replicas and updates it when the manifest changes. It will not detect that the application inside the Pod is failing its health check, because that check is run by the kubelet against the probe configured on the container, not by the Deployment controller.

The Argo CD health check reads the Kubernetes resource status fields. For a Deployment, it reads status.conditions and the rollout status. A Deployment that is Available: False because its Pods are CrashLoopBackOff will surface in Argo CD as Degraded. A Deployment whose Pods are running but whose readiness probe is failing will surface as Progressing and then Degraded if the probe continues to fail.

The operator’s next read is the resource’s own status:

kubectl describe deploy guestbook

which will show Available: False (UnavailableReplicas).

The rollout discipline

The reason an unhealthy deployment reaches the cluster is that the manifest change did not wait for the rollout to complete:

  • Readiness probes configured. Every Deployment should have a readiness probe that actually checks application health.
  • Argo CD sync options. Automated with self-heal=true are the defaults that let the controller react to drift.
  • CI that runs kubectl rollout status against staging. A manifest that validates but fails to roll out is caught at the CI gate.

Flux’s equivalent

Flux reports health through Kustomization and HelmRelease status conditions. A Kustomization that applies successfully but whose child resources are unhealthy surfaces as Ready=False with reason HealthCheckFailed. For Helm, the equivalent is the HelmRelease resource’s status.conditions.

Production discipline

  • Alert on Health Status: Degraded, not on Sync Status: OutOfSync. Degraded is the signal that the application is broken; OutOfSync is the signal that the manifest has drifted.
  • Probe configuration is part of the manifest, not an afterthought. A Deployment without a readiness probe is a Deployment whose health cannot be inferred by the controller.
  • Wait for the rollout, do not sync the next change. A sync cadence faster than the rollout cadence produces a stack of half-applied changes.

Cross-course references

  • Linux for Production Sysadmins - Parts XII (RepoSecurity) and XXXIV (ConfigMgmt) cover the equivalent gap between service-running and service-healthy in systemd unit files.
  • Ansible for Production Sysadmins - Part XXXVII (RepoArch) covers the gap between playbook-applied and application-healthy, which Ansible’s wait_for and uri modules exist to close.

Quiz

Knowledge check · 4 questions

  1. Q1. An Argo CD application reports 'Sync Status: Synced' and 'Health Status: Degraded'. The Deployment has 3/3 replicas running but the readiness probe is failing on every Pod. What is the most accurate reading?

  2. Q2. A Synced application is by definition a Healthy application, because the GitOps controller has confirmed the cluster matches Git.

  3. Q3. Which two commands are the canonical reads when an application shows 'Health Status: Degraded' but 'Sync Status: Synced'?

  4. Q4. Diagnose why a healthy-looking application is failing user requests despite the GitOps controller reporting Synced and Healthy.

    A team deploys a new version of an API server. The Deployment has a readiness probe that checks `/healthz`. The new version has a startup race that causes `/healthz` to return 200 immediately but to throw an exception under load. The Deployment has 3/3 replicas available, the readiness probe is passing, and Argo CD reports Synced and Healthy. User requests are failing with 500 errors.

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