Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXX · GitOps PruningGitOpsPruning

What prune is — Git desired state deletes cluster actual

Advanced⏱ ~22 mingitargocd

What you'll learn

  • Define prune as the controller's contract to delete cluster resources absent from Git
  • Identify what prune deletes and what it does not
  • Explain why prune is both the audit-trail mechanism and the largest blast radius in GitOps
  • Recognise the production consequence of a renamed or removed manifest

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.

Prune is the controller’s contract that the cluster matches Git even when Git shrinks. When a manifest leaves the chart, the controller deletes the corresponding cluster resource. The deletion is committed to Git; the audit trail is clean; the change is reproducible from a single source of truth. The same property that makes prune an audit story - it deletes unconditionally on a diff that removes the resource - is what makes an unintended prune the largest blast radius in GitOps.

The prune contract

The controller reconciles desired state from Git with actual state in the cluster. A resource present in the previous apply inventory but absent from the new render is the prune set. With prune enabled, the controller deletes each resource in the prune set.

argocd app sync "$APP_NAME" --prune

The --prune flag is a per-sync override. The Application’s standing configuration is spec.syncPolicy.automated.prune, set with the --auto-prune flag on argocd app set:

argocd app set "$APP_NAME" --auto-prune

The flag means “prune resources the controller owns on every automated sync”. Individual resources opt out with the argocd.argoproj.io/sync-options: Prune=false annotation.

What prune deletes and what it does not

The boundary of prune is ownership. A resource is owned by a controller when the controller’s inventory tracks it. Argo CD’s inventory is the set of resources it has applied under the Application; Flux’s inventory is the set carrying the kustomize.toolkit.fluxcd.io/name label.

flowchart LR
    A["Git previous render"] --> C["Diff against new render"]
    B["Git new render"] --> C
    C --> D{"In previous, not in new?"}
    D -->|yes| E["Prune candidate"]
    D -->|no| F["No action"]
    E --> G{"Controller owns it?"}
    G -->|yes| H["DELETE from cluster"]
    G -->|no| I["Leave in cluster — orphan"]
    H --> J["Cluster matches Git"]
    F --> J
    I --> J

The right branch of the diagram is the orphan path: the resource stays because no controller claims it. The left branch is the prune path: the controller deletes a resource it owns. The audit trail records both; only the delete is a cluster-side effect.

The production consequence of a renamed manifest

A rename is the most dangerous operation in prune-enabled GitOps. The controller diffs the new render against the previous render, sees a ConfigMap/payment-config absent and a ConfigMap/payments-config present, and concludes: delete the old, create the new. The deletion is silent and fast. The team that wrote the rename discovers the outage when the consumer of the old ConfigMap fails.

Production discipline

  1. Prune is enabled per Application, never cluster-wide. Automated prune (spec.syncPolicy.automated.prune) is an Application-level setting; defaulting it cluster-wide is the configuration error that turns a typo into a production-wide outage.
  2. Every removal is a PR with a deletion-aware reviewer. A reviewer who knows prune is enabled reads the diff as a delete.
  3. The first prune-enabled sync is paired with a namespace backup. Recovery from an unintended delete is the backup; prevention is the audit.
  4. Prune=false is the escape hatch for what must not be deleted. Set it per-Application with argocd app set --sync-option Prune=false, or per-resource with the argocd.argoproj.io/sync-options: Prune=false annotation.

Cross-course references

  • Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXIX-04 (Prune and the blast radius) is the foundation this lesson deepens; Part LXXV (Drift) is the complementary failure mode where the cluster diverges from Git.
  • Kubernetes for Production Sysadmins - Part VII (Workloads) is the resource class prune operates on; Part XVI (Operators and Controllers) covers the reconcile loop prune extends.

Quiz

Knowledge check · 4 questions

  1. Q1. An engineer renames a Deployment from `api-v1` to `api-v2` in the chart and commits. With prune enabled, what does the controller do on the next sync?

  2. Q2. Prune deletes every resource in the cluster that is absent from the Git render, regardless of whether the controller ever owned it.

  3. Q3. Name the two production-grade properties that prune gives a GitOps workflow, and the one failure mode that makes it the largest blast radius in the controller.

  4. Q4. Diagnose why a ConfigMap disappeared after a routine chart refactor and recommend the prevention.

    A team has prune enabled on a production Application. A developer refactors a Helm chart to rename `payment-config` to `payments-config`, merging the PR through a single reviewer. The next automated sync deletes `payment-config` and creates `payments-config`. A second Deployment in the same namespace consumes the ConfigMap by its old name and crashes. The team has no namespace backup. Audit trail is clean (one commit, one PR, one sync), but the deletion was unintended.

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