Skip to content
Intellira
ArgoCDmedium severity

OutOfSync

An ArgoCD app shows OutOfSync when live cluster state deviates from the target state in Git. How to read the diff, find the real source of drift, and fix it.

Written by Intellira Engineering, Editorial team

What OutOfSync means

In ArgoCD, sync status answers one question: does the live state in the cluster match the target (desired) state declared in Git? Per the official docs, "a deployed application whose live state deviates from the target state is considered OutOfSync" (Argo CD overview). ArgoCD "reports & visualizes the differences, while providing facilities to automatically or manually sync the live state back to the desired target state" (Argo CD overview).

This is independent of health status — an app can be Healthy and OutOfSync at the same time. OutOfSync has several distinct causes, and they need different fixes. Some are a missing field (drift), some are a missing resource (prune or TTL), some are a field that ArgoCD will never win against (webhooks, controllers, server-side defaults). Identify the mechanism before you sync.

Diagnose it

Start with the diff. It is the single most useful command here:

argocd app get <app-name>
argocd app get <app-name> --refresh   # force a fresh comparison
argocd app diff <app-name>
===== apps/Deployment /payments/payments-api ======
<     replicas: 3        # desired (Git)
>     replicas: 5        # live (cluster)

Warning: Always run argocd app diff before syncing. A blind sync overwrites live drift with Git — correct if the drift was an accidental manual edit, but destructive if someone is mid-incident and manually scaled up a deployment.

Then classify what the diff is telling you:

What the diff showsLikely mechanismJump to
A field you set in Git differs (e.g. replicas, image)Manual edit, or a controller owns that fieldDrift
Fields you never wrote (sidecars, injected labels/annotations, cpu/memory defaults)Mutating webhook or server-side API defaultsInjected fields
Diff is empty but app still OutOfSyncA resource is pending prune, or a Job/Workflow was TTL-deletedMissing resources
Extra resources generated by another toolExtraneous resource / instance-label conflictExtraneous
OutOfSync only during a deploy, clears on its ownMulti-wave sync in progressSync in progress

To inspect field ownership (who last wrote each field), read managedFields:

kubectl get deployment payments-api -n payments -o yaml --show-managed-fields

1. Drift on a field you manage

A manual kubectl edit/kubectl scale, or a controller (HPA, VPA) that owns a field, changed something ArgoCD also declares in Git. If it is a one-off manual edit, decide direction and sync. If a controller re-mutates the field on every apply, the app flaps back to OutOfSync.

Diagnose: argocd app diff shows a field you control. Check managedFields (above) — if the manager is kube-controller-manager or an HPA, the controller owns it, not a human.

Fix (manual edit): decide which side is correct.

# Git is correct (accidental edit): overwrite live
argocd app sync <app-name>
# Live is correct (deliberate, uncommitted change): commit it to Git first, then sync

Fix (controller owns the field): add an ignoreDifferences entry so ArgoCD stops treating it as drift. ArgoCD lets you "optionally ignore differences of problematic resources" using "RFC6902 JSON patches" (jsonPointers) and "JQ path expressions" (jqPathExpressions) (Diff Customization).

spec:
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas

Critical: ignoreDifferences by default only changes the diff — it does not change what gets applied. ArgoCD "uses the ignoreDifferences config just for computing the diff … However during the sync stage, the desired state is applied as-is" (Sync Options). So with auto-sync selfHeal: true, ArgoCD will still push replicas: 3 and fight the HPA. To make sync respect the ignore rule, add the RespectIgnoreDifferences=true sync option — it works by "calculating and pre-patching the desired state before applying it in the cluster" (Sync Options).

spec:
  syncPolicy:
    syncOptions:
      - RespectIgnoreDifferences=true

If you run automated sync with selfHeal: true, ArgoCD will reapply Git on its own: "if the selfHeal flag is set to true, then the sync will be attempted again after self-heal timeout (5 seconds by default)" (Automated Sync Policy). Pause auto-sync (or add the ignore + RespectIgnoreDifferences) before debugging a flapping field, or self-heal will fight your manual changes.

2. Fields injected by webhooks or the API server

The diff shows fields you never wrote in Git: an injected sidecar container, labels/annotations added by a policy controller, or resources.requests the API server filled in as a default. A mutating admission webhook modifies resources as they pass through the API server; client-side diff has no way to predict those fields, so the app reports OutOfSync forever.

Diagnose: the diff adds fields that aren't in your manifest, and they reappear after every sync. kubectl get ... --show-managed-fields shows a manager other than ArgoCD (e.g. a webhook's service account or kube-apiserver).

Fix (preferred — server-side diff): ArgoCD's server-side diff "will execute a Server-Side Apply in dryrun mode for each resource" so "Kubernetes Admission Controllers will participate in the diff calculation" (Diff Strategies). Note: "Server-Side Diff does not include changes made by mutation webhooks by default" — for webhook-injected fields you must also opt in (Diff Strategies).

metadata:
  annotations:
    argocd.argoproj.io/compare-options: ServerSideDiff=true,IncludeMutationWebhook=true

IncludeMutationWebhook "is only effective when Server-Side Diff is enabled" (Diff Strategies).

Fix (alternative — ignore by field manager): if you can't enable server-side diff, ignore everything a given manager owns. managedFieldsManagers ignores "differences from fields owned by specific managers defined in metadata.managedFields in live resources" (Diff Customization).

spec:
  ignoreDifferences:
    - group: ''
      kind: Pod
      managedFieldsManagers:
        - my-mutating-webhook

As in section 1, pair this with RespectIgnoreDifferences=true if you also need sync (not just the diff) to leave the field alone.

3. Empty diff: pending prune or TTL-deleted resources

The app is OutOfSync but argocd app diff shows no field differences. The drift is a missing or extra resource, not a changed field.

Pending prune. A resource was removed from Git, but pruning is disabled, so it still lives in the cluster: "The app will be out of sync if Argo CD expects a resource to be pruned" (Sync Options).

  • Diagnose: argocd app get <app-name> lists a resource with OutOfSync and a prune/delete intent; the resource is absent from current Git.
  • Fix: either delete it intentionally with argocd app sync <app-name> --prune, or, if you deliberately keep it, mark it Prune=false and silence the status with the IgnoreExtraneous compare option (see section 4).

TTL-deleted Jobs/Workflows. A Job with spec.ttlSecondsAfterFinished (or an Argo Workflow ttlStrategy) is auto-deleted by the cluster after it completes. Git still declares it, the cluster no longer has it → permanent OutOfSync.

  • Diagnose: the OutOfSync resource is a completed Job/Workflow that no longer exists in the cluster, and its manifest sets a TTL.
  • Fix: drive cleanup through an ArgoCD resource hook with a delete policy (argocd.argoproj.io/hook-delete-policy: HookSucceeded) instead of a Kubernetes TTL, so ArgoCD — not the cluster — owns the deletion (Resource Hooks).

4. Extraneous or tool-generated resources

Another tool (Kustomize commonLabels, a generator, an operator) created resources or labels ArgoCD did not, and they show up as drift. A classic case is a label conflict: a tool adds app.kubernetes.io/instance, which ArgoCD also uses for tracking.

Diagnose: the OutOfSync resources are ones your tooling generates, or the diff is on the tracking label. argocd app get flags them as extra/OutOfSync.

Fix (exclude from sync status): annotate the resource so it no longer affects overall status. IgnoreExtraneous lets you "exclude resources from the app's overall sync status … if they are generated by a tool" — but "this only affects the sync status. If the resource's health is degraded, then the app will also be degraded" (Compare Options).

metadata:
  annotations:
    argocd.argoproj.io/compare-options: IgnoreExtraneous

Fix (label conflict): change the tracking label key. Set application.instanceLabelKey in argocd-cm; the docs recommend argocd.argoproj.io/instance. Note "when you make this change your applications will become out of sync and will need re-syncing" (Argo CD FAQ).

5. Transient OutOfSync during a multi-wave sync

If the app uses sync waves, it is normally OutOfSync while the sync runs and clears once all waves complete. Sync waves are set with the argocd.argoproj.io/sync-wave annotation; ArgoCD applies lower wave numbers first and "there is currently a delay between each sync wave in order to give other controllers a chance to react" (Sync Phases and Waves).

Diagnose: OutOfSync only appears during a deploy and resolves itself. Check argocd app get — an early-wave resource may be stuck Unhealthy.

Fix: usually none needed — wait for the sync to finish. But "because an application can have resources that are unhealthy in the first wave, it may be that the app can never get to healthy" (Sync Phases and Waves), which stalls all later waves. If the app is stuck, find the unhealthy early-wave resource and fix that — the OutOfSync status downstream is a symptom, not the cause.

How Intellira diagnoses this

Intellira fetches the application's sync status and the field-level diff through the ArgoCD MCP server, then correlates each drifted item with its likely mechanism — a recent kubectl change, an HPA that owns replicas, a webhook-injected field, a resource pending prune, or a wave still in progress. Instead of just reporting "OutOfSync," it points at which resource or field drifted and whether a sync is safe or would overwrite an intentional live change.

Sources

By Intellira Engineering. AI-assisted draft, reviewed by the Intellira engineering team; claims cited inline; last verified 2026-06-02.

Frequently asked questions

Does OutOfSync mean my application is broken?
Not necessarily. OutOfSync is a comparison result, not a health state. It means the live cluster state deviates from the target state declared in Git. The app can still be Healthy and serving traffic while OutOfSync — for example when someone made a manual kubectl edit that ArgoCD has not reconciled yet.
Why does my app go OutOfSync immediately after every sync?
Something is mutating the resource after ArgoCD applies it — an HPA changing replicas, an admission webhook injecting fields, or the API server adding server-side defaults. ignoreDifferences alone does not stop the flapping, because by default it only affects the diff, not the apply; the field is still pushed every sync. You also need RespectIgnoreDifferences=true (or server-side diff for webhook/default-injected fields).
What is the difference between OutOfSync and Sync Failed?
OutOfSync is a passive observation that Git and the cluster differ. Sync Failed means ArgoCD attempted to apply the desired state and the apply itself errored. OutOfSync can be resolved by a successful sync; Sync Failed requires fixing the underlying apply error first.
Why is my app OutOfSync when argocd app diff shows nothing?
Two common cases: a resource ArgoCD expects to prune is blocked by Prune=false (the missing-deletion counts as drift), or a Job with ttlSecondsAfterFinished was auto-deleted after completion so the live resource is gone. Both keep the app OutOfSync even though no field differs.

Related errors

Find the root cause of OutOfSync on your stack

Connect read-only and Intellira correlates the change behind it across Bitbucket, Jenkins, ArgoCD and Kubernetes — with the evidence to prove it.