Argo CD Self-Heal Reconciliation Boundary with Server-Side Apply Idempotency
0 reputation · 12 Dec 2022, 23:53 UTC
0 reputation · 12 Dec 2022, 23:53 UTC
Argo CD's automated sync policy with selfHeal enabled triggers periodic reconciliation by comparing the cached desired manifest against the live Kubernetes state. When a sync retry occurs without changes to the application source, the server-side apply leverages Kubernetes API idempotency to avoid duplicate writes. However, the precise condition under which a retry transitions from an idempotent no-op to a full reconciliation remains ambiguous when external modifications alter resources between retry cycles. This boundary involves Argo CD's operationState tracking, the sync algorithm's state-detection logic, and how Kubernetes strategic merge patches are evaluated during reconciliation.
33275 reputation · 13 Dec 2022, 00:06 UTC
Argo CD decides whether a self‑heal cycle is a no‑op or a full reconcile by comparing the cached desired manifest (the version stored in its internal state, sourced from Git) with the live Kubernetes state it observes via the API. The decision is made before any server‑side apply call is issued.
operationState: SYNCED. The next self‑heal tick sees no drift and therefore performs no reconciliation work – the server‑side apply is never invoked, or if invoked it receives an empty patch and becomes a no‑op thanks to Kubernetes’ idempotency.apply‑prune strategy – the comparison yields a non‑empty strategic merge patch. Argo CD then updates operationState to OUT_OF_SYNC. On the following self‑heal tick it executes a full reconcile: it recomputes the patch and issues a server‑side apply call. Because server‑side apply is idempotent, re‑applying the same patch does not cause duplicate writes, but the reconcile still runs.Argo CD persists operationState in the application’s status (status.sync). This field is:
SYNCED after a successful sync when the cached and live states match.OUT_OF_SYNC whenever a drift is detected during a sync or self‑heal evaluation.argocd app set --sync-option Prune=false or clearing the status) – doing so can temporarily suppress a full reconcile until the next external change causes a new drift detection.Thus, adjusting operationState does not change the underlying comparison logic; it only changes the starting point for the next self‑heal evaluation. If you force operationState: SYNCED while the live state has drifted, the next self‑heal will still detect the mismatch and transition to OUT_OF_SYNC before deciding to reconcile.
operationState is SYNCED. Self‑heal finds zero‑size patch → skips apply.operationState becomes OUT_OF_SYNC. Self‑heal runs the reconcile path, computes a patch, and invokes server‑side apply.argocd version --client
argocd version --server--loglevel debug in the controller deployment) and watch the logs during a self‑heal interval.operationState: SYNCED – indicates a no‑op self‑heal.operationState: OUT_OF_SYNC – indicates drift detection.patch size: 0 bytes vs. patch size: N bytes – shows whether a non‑empty patch was generated.kubectl annotate pod/foo bar=baz) and observe that the next self‑heal logs OUT_OF_SYNC and a non‑zero patch. Reverting the annotation should bring the next self‑heal back to SYNCED with a zero‑size patch.The exact set of fields Argo CD includes in its equality check depends on the ignoreDifferences configuration and any custom jsonPath exclusions. If you have non‑default ignore rules, please confirm whether the externally modified field is covered by those rules, as that determines whether the change triggers drift.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.