Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXX · GitOps PruningGitOpsPruning

Prune versus orphan — the two failure modes for absent manifests

Advanced⏱ ~24 mingitargocd

What you'll learn

  • Distinguish prune from orphan at the controller level
  • Identify the production failure each one produces
  • Map a symptom to the right failure mode
  • Apply the correct detection command for each case

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.

Two failure modes produce the same dashboard symptom: the cluster does not match Git. The first is prune — the resource was tracked and was deleted. The second is orphan — the resource was never tracked, or was created by a controller that is no longer managing it, and the cluster still carries it. The remediation paths are opposite. Treating orphan as prune deletes a resource that is needed. Treating prune as orphan lets drift grow.

The two contracts

A prune is a deletion executed by the controller because the resource was in the previous render and is absent from the new render. An orphan is a cluster resource that the controller has not claimed because no controller has the metadata that says “this is mine”.

flowchart LR
    R["Git render"] --> D["Diff vs cluster"]
    C["Cluster actual"] --> D
    D --> P{"In Git, not in cluster?"}
    D --> Q{"In cluster, not in Git?"}
    P -->|yes| X["Create"]
    Q -->|yes| T{"Controller owns it?"}
    T -->|yes| Y["Prune — DELETE"]
    T -->|no| Z["Orphan — leave"]

The two questions are independent: a resource can be in Git but not in the cluster (a create), in the cluster but not in Git (either a delete or an orphan), in both (no action), or in neither (an unrelated resource that the controller ignores).

What prune catches

Prune catches the removed-from-manifest case:

  • A chart drops a deprecated resource.
  • A refactor moves a manifest to a different chart.
  • A rename produces a diff with one less and one more object.
  • A values-merge evaluates to an empty list where the old values produced a non-empty list.

In every case the resource was tracked, the new render no longer references it, and the controller’s prune set contains it. With prune enabled, the controller deletes the cluster resource.

argocd app manifests "$APP_NAME"

The argocd app manifests command prints the rendered manifests the controller would apply. If a manifest is missing from this output, the resource is in the prune set.

What orphan catches

Orphan catches the never-tracked case:

  • A kubectl apply from an operator outside the GitOps flow.
  • A Helm install from a different chart not managed by the Application.
  • A resource created by an admission controller, a controller bootstrap, or a disaster-recovery runbook.
  • A resource whose ownership label was removed by an unrelated process.

In every case the resource exists in the cluster, the GitOps controller has no metadata that says “this is mine”, and the controller leaves it alone. Argo CD’s UI surfaces these as Extra resources on the Application view; Flux has no automatic orphan detection by default.

Detecting each case

Detection is two commands:

  1. argocd app manifests "$APP_NAME" — the controller’s view of the desired state.
  2. kubectl get all -n "$NAMESPACE" -l app.kubernetes.io/instance="$APP_NAME" — the cluster’s view of the Application’s resources.

If a resource appears in the kubectl output but not in the manifests output, the next sync will see it. With prune enabled, the controller will delete it. Without prune, the resource stays as an orphan. The classification is the difference between a deletion and a piece of unmanaged infrastructure.

Production discipline

  1. Prune is a destructive operation and must be enabled deliberately. The default for new Applications is prune disabled.
  2. Orphan detection is a separate process. Even without prune, the team should diff cluster resources against the controller’s manifest list at every release.
  3. A divergence alert is a classification step, not a remediation step. Resolve “what kind of divergence is this?” before deciding what to do.

Cross-course references

  • Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXX-01 (What prune is) is the foundation this lesson contrasts with; Part LXXV (Drift) covers the broader divergence family.
  • Kubernetes for Production Sysadmins - Part VII (Workloads) is the resource class both failure modes operate on; Part XVI (Operators and Controllers) explains the ownership metadata.

Quiz

Knowledge check · 4 questions

  1. Q1. A team notices a Deployment that is not in the Git manifest but is running in the cluster. The Application has prune enabled. What happens on the next sync?

  2. Q2. Argo CD's UI surfaces orphan resources under the Application's `Extra` section, and Flux has the same default orphan-detection behaviour.

  3. Q3. Name the two commands you would run to classify a divergence alert as prune versus orphan, and what each tells you.

  4. Q4. Classify the divergence and recommend the right remediation.

    A production Application reports `OutOfSync` with one missing resource. The team runs `argocd app manifests $APP_NAME` and sees a Deployment named `payment-worker` in the output. They run `kubectl get deploy -n payments -l app.kubernetes.io/instance=payment-api` and see a Deployment named `payment-worker` AND a Deployment named `payment-worker-debug`. The Application has prune enabled. Which is the prune case and which is the orphan case, and what is the right action?

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