DevOps

ArgoCD stuck OutOfSync — every cause and fix

Diagnose and fix ArgoCD Applications showing OutOfSync. Manifest drift, RBAC issues, sync policies, and self-heal misconfigurations covered.

ArgoCD showing an Application as OutOfSync means the live cluster state differs from what Git specifies. Sometimes this is expected. Often it indicates a real problem. Here are all the causes and how to fix each.

Understand what OutOfSync actually means

ArgoCD compares:

  • Desired state: Kubernetes manifests in your Git repo
  • Live state: what actually exists in the cluster

If they differ, status = OutOfSync. Sync (manual or auto) applies the Git version to the cluster.

Healthy status is Synced + Healthy. Anything else needs investigation.

Diagnose first — see the difference

argocd app diff <app-name>

Or in the UI: click the app → “APP DIFF” tab.

This shows exactly what changed between Git and cluster. The fix depends on which side has the “right” version.

Cause 1 — Someone edited resources directly with kubectl

Most common cause. A developer ran kubectl edit deployment to test something. That change isn’t in Git. ArgoCD sees drift.

Two options:

Option A — accept the direct change and update Git:

kubectl get deployment <name> -o yaml > my-deployment.yaml
# edit to remove runtime fields, commit to git
git add my-deployment.yaml
git commit -m "Sync recent deployment change"
git push

Option B — revert cluster to match Git:

argocd app sync <app-name>

Enable self-heal to prevent recurrence:

spec:
  syncPolicy:
    automated:
      selfHeal: true

Cause 2 — CRDs missing when app tries to sync

Application uses Custom Resources (like Prometheus’s ServiceMonitor) but the CRD isn’t installed yet.

Symptom: sync fails with ensure CRDs are installed first.

Fix: install CRDs before the Application. Two approaches:

Approach A — sync waves (order of application):

metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "-1"  # applies before wave 0

Approach B — install CRDs manually first, then Application:

kubectl apply -f https://path/to/crds.yaml
argocd app sync <app-name>

Cause 3 — Namespace missing

Application references a namespace that doesn’t exist.

Symptom: sync fails with namespaces "foo" not found.

Fix: enable auto-creation:

spec:
  syncPolicy:
    syncOptions:
    - CreateNamespace=true

Or create the namespace manually:

kubectl create namespace foo

Cause 4 — Immutable field changed

Some Kubernetes fields cannot be updated in-place (.spec.selector on Deployments, .spec.storageClassName on PVCs, etc.).

Symptom: sync fails with field is immutable.

Fix: the resource must be replaced. Add sync option:

spec:
  syncPolicy:
    syncOptions:
    - Replace=true

Or manually delete and recreate:

kubectl delete <resource-type> <name> -n <namespace>
argocd app sync <app-name>

Warning: this causes downtime for stateful workloads. Handle carefully.

Cause 5 — RBAC blocking ArgoCD’s ServiceAccount

ArgoCD tries to create resources but its ServiceAccount lacks permissions.

Symptom: sync fails with forbidden: user "system:serviceaccount:argocd:argocd-application-controller" cannot create.

Diagnose:

kubectl auth can-i create deployments \
  --as system:serviceaccount:argocd:argocd-application-controller \
  -n <target-namespace>

Fix: grant permissions with a ClusterRoleBinding:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: argocd-can-deploy
subjects:
- kind: ServiceAccount
  name: argocd-application-controller
  namespace: argocd
roleRef:
  kind: ClusterRole
  name: cluster-admin  # or a narrower role
  apiGroup: rbac.authorization.k8s.io

For production, use a narrower role than cluster-admin. Grant only what’s needed.

Cause 6 — Sync policy is manual (not auto)

Application status says OutOfSync but nothing happens.

Check:

argocd app get <app-name> | grep "sync policy"

If it says Sync Policy: <none>, syncs are manual.

Fix: enable auto-sync:

spec:
  syncPolicy:
    automated:
      prune: true      # delete resources not in Git
      selfHeal: true   # revert manual cluster changes

Cause 7 — Prune disabled — orphaned resources

Removed a manifest from Git but the resource still exists in cluster. ArgoCD shows OutOfSync but won’t delete without prune enabled.

Fix: enable prune in sync policy:

spec:
  syncPolicy:
    automated:
      prune: true

Manual prune:

argocd app sync <app-name> --prune

Cause 8 — Diff on runtime-only fields

Kubernetes adds fields at runtime (.status, .metadata.resourceVersion, .metadata.uid). Sometimes ArgoCD sees these as drift.

Fix: ignore differences using resource-specific overrides:

spec:
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/replicas   # ignore HPA-managed replica count

Common candidates:

  • spec/replicas when using HPA
  • metadata/annotations/deployment.kubernetes.io/revision
  • Fields written by admission controllers

Cause 9 — Helm chart version mismatch

App references a Helm chart version. Chart was updated in git or repo but not synced.

Symptom: OutOfSync after chart upgrade.

Fix: hard refresh + sync:

argocd app get <app-name> --hard-refresh
argocd app sync <app-name>

Cause 10 — Kustomize / Helm values changed but not detected

Configuration in overlays or values files changed but ArgoCD hasn’t noticed.

Fix: force refresh:

argocd app get <app-name> --refresh

Or in the UI: click “Refresh” button on the Application.

Cause 11 — Waiting on parent Application (App-of-Apps pattern)

Child app waiting for parent to sync first.

Fix: sync the parent Application first:

argocd app sync <parent-app>

Cause 12 — Sync retry limit hit

App tried to sync many times and failed. Now waiting.

Fix: manual retry:

argocd app sync <app-name> --retry-limit 5 --retry-backoff-duration 30s

The universal debug flow

Every OutOfSync issue, run this:

# See status
argocd app get <app-name>

# See what differs
argocd app diff <app-name>

# See sync history
argocd app history <app-name>

# Force refresh
argocd app get <app-name> --refresh

# Try sync with verbose logging
argocd app sync <app-name> --loglevel debug

Root cause usually visible in first 3 commands.

Prevention checklist

Set up new Applications with these defaults:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/user/repo
    targetRevision: HEAD
    path: apps/myapp
  destination:
    server: https://kubernetes.default.svc
    namespace: myapp
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
    - PrunePropagationPolicy=foreground
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

This covers 80% of OutOfSync causes preventatively.

Reproduce this yourself

Free ArgoCD environment: install on Killercoda (killercoda.com/playgrounds/scenario/kubernetes):

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml --server-side

# Wait for pods
kubectl -n argocd get pods

# Get admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

# Port-forward
kubectl port-forward svc/argocd-server -n argocd 8080:443

Create an Application, edit resources with kubectl edit, watch it become OutOfSync. Then try each fix above.

When OutOfSync is actually fine

Some legitimate cases where OutOfSync is expected temporarily:

  • Application just deployed, still initializing
  • Helm chart update in progress
  • Rolling update mid-flight
  • HPA-managed replica differences

These resolve on their own within minutes. If OutOfSync persists >5 minutes, investigate.

Bottom line

OutOfSync = Git and cluster disagree. Diagnose which side has the correct version. Sync from the right direction. Enable auto-sync + self-heal to prevent recurrence.

The 12 causes above cover ~99% of real-world scenarios. Match the symptom, apply the fix.

Recommended

DevOps YAML Pack

36 production-ready configs — Kubernetes, Docker Compose, GitHub Actions, Terraform, Helm, Ansible. Every file heavily commented. Copy, paste, ship.

Get the pack — ₹499 →
Never miss an article