Git, CI/CD & GitOpsLXXX · GitOps PruningGitOpsPruning
What prune is — Git desired state deletes cluster actual
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
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
- 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. - Every removal is a PR with a deletion-aware reviewer. A reviewer who knows prune is enabled reads the diff as a delete.
- The first prune-enabled sync is paired with a namespace backup. Recovery from an unintended delete is the backup; prevention is the audit.
Prune=falseis the escape hatch for what must not be deleted. Set it per-Application withargocd app set --sync-option Prune=false, or per-resource with theargocd.argoproj.io/sync-options: Prune=falseannotation.
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
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?
Q2. Prune deletes every resource in the cluster that is absent from the Git render, regardless of whether the controller ever owned it.
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.
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.