Git, CI/CD & GitOpsCV · GitOps Anti-PatternsUnsafePrune
Unsafe prune — automated deletion without a diff, a sandbox, or an opt-out
What you'll learn
- Identify the three controls that must accompany an enabled prune policy
- Apply dry-run diff before every prune-enabled sync and read the delete lines
- Configure a sandbox cluster for first-prune verification of a refactored chart
- Use the Prune=false sync option override for the Application or resource that must not be deleted
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
The default sync policy in a freshly-installed Argo CD or Flux is prune-disabled. The first thing many teams do is flip it on, because the controller has reconciled adds and updates for weeks and the symmetry seems obvious. It is not. Prune is a deletion primitive; the controller applies it on every sync, without prompting.
The three controls prune needs
Prune is survivable when paired with three controls. Remove any one and the pattern becomes unsafe.
flowchart LR
A["Prune-enabled Application"] --> B["Diff"]
B --> C["Sandbox sync"]
C --> D["Production sync"]
D --> E["Cluster matches Git"]
B -.->|"missing"| F["Typo deletes needed resource"]
C -.->|"missing"| G["Refactor breaks consumers"]
D -.->|"missing"| H["Hotfix reverted by next sync"]
- Dry-run diff. Before every prune-enabled sync, the
team runs
argocd app diff $APP_NAME --server-side-generate. The diff prints every create, update, and delete the sync will execute. - Sandbox cluster. The first prune-enabled sync of an Application is run against a non-production cluster that mirrors the production structure but carries no real traffic.
- Prune=false opt-out. The Application that must not
prune carries
--sync-option Prune=false(set withargocd app set); the single resource that must survive carries theargocd.argoproj.io/sync-options: Prune=falseannotation.
The team that skips all three runs argocd app sync $APP_NAME --prune and trusts the chart. The team that has
all three runs the diff, reads the delete lines, confirms
the sandbox behaviour, and only then runs the production
sync.
Why the diff is the cheapest control
The diff runs in seconds, has no side effects, and produces
the exact delete list the next sync will execute. A team
that skips the diff on a prune-enabled sync has chosen to
trust the chart without verifying it. The diff is also the
audit trail: a CI job that runs argocd app diff on every
pull request produces a record of what the sync would do.
argocd app diff "$APP_NAME" --server-side-generate
The --server-side-generate flag matches the apply mode the
controller will use. The output includes a line for each
delete candidate with the resource’s Kind, name, and
namespace.
The sandbox cluster as first-prune verification
A sandbox cluster is a non-production Kubernetes cluster where the first prune-enabled sync is run end-to-end. The cluster mirrors production’s structure but carries no real traffic. A failed sandbox sync catches the unintended delete before it reaches production.
argocd app sync "$APP_NAME" \
--prune \
--server "$SANDBOX_ARGOCD"
A sandbox sync that deletes an unintended resource costs the team an hour; the same deletion in production costs the team a customer.
The Prune=false escape hatch
The escape hatch for the Application or resource that must not be deleted:
argocd app set "$APP_NAME" \
--sync-option Prune=false
The flag sets the Prune=false sync option on the
Application’s standing configuration: syncs stop deleting
its resources until the option is removed with argocd app set "$APP_NAME" --sync-option '!Prune=false' (the !
prefix removes the option). A single
resource opts out with the
argocd.argoproj.io/sync-options: Prune=false annotation.
Flux supports the same standing pattern through
spec.prune: false on the Kustomization.
A team that abandons pruning wholesale to protect a single
risky resource has lost the prune discipline; the
Application-level option and the per-resource annotation
are the scoped opt-outs, and a manual argocd app sync
deletes nothing unless it is passed --prune.
Production discipline
- Diff is paired with every prune-enabled sync.
- The first prune-enabled sync of an Application runs in a sandbox first.
Prune=falseis a scoped, reversible opt-out.- Branch protection is the upstream safety.
Cross-course references
- This course, Part LXXX (GitOpsPruning) - the prune model, the safety mechanisms, the incident walkthrough.
- This course, Part LXXXVII (IncidentGitOps) - break-glass procedures and the reconcile-back requirement.
Quiz
Knowledge check · 4 questions
Q1. An Application is configured to prune. A chart refactor removes a manifest the cluster still consumes. What is the cheapest way to confirm the next sync will delete only the intended resources?
Q2. Setting `Prune=false` on an Application permanently disables prune for all future syncs.
Q3. Name the three controls that must accompany an enabled prune policy.
Q4. Combine the three controls into a procedure for a chart refactor that removes two deprecated resources.
A team is preparing a chart refactor that removes two deprecated ConfigMaps. The Application is configured with automated sync and `--auto-prune`. The team needs to land the refactor without deleting a third ConfigMap that is also consumed by an admission controller outside the chart's inventory.
Passing score: 75%. Answers are checked in this browser.