Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXX · GitOps PruningGitOpsPruning

Orphaned resources and the cluster — what survives when the manifest disappears

Advanced⏱ ~25 mingitargocd

What you'll learn

  • Define an orphan in GitOps terms
  • Identify why orphans accumulate in long-running clusters
  • Detect orphans with kubectl and the controller's manifest output
  • Apply the production rules for managing orphans

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.

An orphan is a cluster resource that no GitOps controller tracks. The resource exists in the cluster, the GitOps controller has no metadata that says “this is mine”, and the controller will never prune it, never update it, and never reconcile it. The orphan survives every sync, accumulates over the lifetime of the cluster, and breaks the GitOps promise that Git is the source of truth - because the cluster now contains a resource whose truth lives nowhere.

Why orphans accumulate

Orphans come from four sources. Each one is a small decision that adds up to a long-running cluster where the cluster’s actual state is larger than the union of every Application’s manifests.

flowchart TD
    A["kubectl apply by operator"] --> O["Orphan cluster resource"]
    B["Helm install outside GitOps"] --> O
    C["Disaster recovery runbook"] --> O
    D["Controller decommissioned, resource left behind"] --> O
    O --> E["Cluster > sum of manifests"]
    E --> F["Git is no longer source of truth"]

The four sources share a pattern: the resource was created by a process that did not register the resource with a GitOps controller. Argo CD’s tracking metadata is app.kubernetes.io/instance plus the Application’s UID; Flux’s tracking metadata is the kustomize.toolkit.fluxcd.io/name label. A resource missing both sets of metadata is invisible to both controllers.

What orphans survive

Orphans survive:

  • Every prune — the controller does not know they exist.
  • Every sync — the controller’s render does not contain them, so the diff produces no deletion action.
  • Every drift correction — the controller’s desired state does not mention them, so the self-heal does not touch them.
  • Every rename — a rename of the related Application does not affect orphans because they have no Application binding.

The orphan is, in effect, a permanent piece of cluster state that no automation will touch. An engineer with kubectl delete can remove it. An admission webhook can mutate it. A node failure can restart it. But no controller will reconcile it against any declared desired state, and no Git commit records its existence.

Detecting orphans

Orphan detection is a two-command process:

argocd app manifests "$APP_NAME"
kubectl get all -n "$NAMESPACE" \
  -l app.kubernetes.io/instance="$APP_NAME" \
  -o json

A resource in the kubectl output but not in the manifests output is either an orphan or a resource owned by a different controller. The classification step is to check whether the resource carries the controller’s tracking metadata:

kubectl get deploy payment-worker-debug -n payments \
  -o jsonpath='{.metadata.labels}'

A label set that does not include app.kubernetes.io/instance=$APP_NAME and does not include kustomize.toolkit.fluxcd.io/name=$APP_NAME is an orphan from both controllers’ points of view.

The production rules

  1. Orphans are documented or deleted within a release. A new Application’s audit must include a sweep for orphans in its target namespace; the team decides whether to claim each one into the chart, delete it, or document its unmanaged status.
  2. Off-GitOps operations are forbidden in production. A kubectl apply from an operator’s terminal must be followed by a commit that adds the manifest to the chart. The exception is an incident; the rule is “every resource has a Git home”.
  3. Helm installs from CI or runbooks must register the resource with the controller. A chart that installs via Helm outside the GitOps flow is an orphan factory.
  4. Orphan detection is a periodic audit, not a one-time cleanup. A quarterly review of every production namespace against every Application’s manifests is the discipline that keeps the Git source-of-truth promise intact.

Cross-course references

  • Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXX-02 (Prune versus orphan) is the foundation this lesson extends; Part LXXV (Drift) is the broader divergence family.
  • Kubernetes for Production Sysadmins - Part VII (Workloads) is the resource class orphans are; Part IX (Namespaces and Quotas) is where the per-namespace audit operates.

Quiz

Knowledge check · 4 questions

  1. Q1. Six months after a successful GitOps rollout, the team notices an unmanaged Deployment in the production namespace. The Application has prune enabled and shows `Synced`. Why is the Deployment still there?

  2. Q2. An orphan resource is not automatically detected and surfaced by both Argo CD and Flux in their default configurations.

  3. Q3. Name the two labels that mark a cluster resource as tracked by a GitOps controller, and the kubectl command that shows whether a Deployment carries them.

  4. Q4. Diagnose why an orphan Deployment survived a year of prune-enabled syncs and recommend the durable fix.

    A team runs a production Application with prune enabled. During a one-off incident six months ago, an on-call engineer ran `kubectl apply -f debug-pod.yaml` to investigate a memory leak and forgot to clean up. The debug Deployment is still running, has been excluded from every load balancer, and consumes a small amount of node resources. The Application shows `Synced` because the controller's tracked resources match the render. The orphan is invisible to automated reconciliation.

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