🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

ArgoCD Application Stuck Progressing: Fix in 5 Minutes

ArgoCD Application stuck in Progressing status forever, never reaching Healthy? Here is exactly how to diagnose stuck rollouts, missing health checks, hook failures, and resource hooks that never complete.

Shubham4 min read
Share:Tweet

Progressing is ArgoCD's honest way of saying "I applied the manifests, but I'm still waiting for Kubernetes to confirm they're actually healthy." When it never resolves, the cause is almost always in what ArgoCD is waiting on, not in ArgoCD itself.

Step 1: See What ArgoCD Is Actually Waiting For

bash
argocd app get myapp
 
# Health Status:  Progressing
# Sync Status:    Synced
 
argocd app resources myapp
# Shows per-resource health — this tells you WHICH resource is stuck,
# not just that the app overall is stuck
bash
# More detail per resource
kubectl get deployment myapp -n production -o jsonpath='{.status.conditions}' | jq .

Cause 1: Deployment Never Reaches Available Replicas

bash
kubectl get pods -n production -l app=myapp
# NAME              READY   STATUS             RESTARTS
# myapp-7f9c-abc12  0/1     ImagePullBackOff   0

ArgoCD's default health check for a Deployment waits for spec.replicas to match status.availableReplicas. If pods never come up, ArgoCD waits forever — it has no timeout by default.

Fix — find and fix the actual pod issue first (this is a symptom, not the cause):

bash
kubectl describe pod myapp-7f9c-abc12 -n production
# Then fix based on the real reason: bad image tag, resource limits, etc.

Once pods actually become Ready, ArgoCD's health check re-evaluates automatically within its next reconcile loop — no ArgoCD-specific action needed.

Cause 2: Custom Health Check Never Returns Healthy

If you have a custom Lua health check for a CRD (common with Argo Rollouts, cert-manager Certificates, or other operators), a bug in that Lua script can leave the resource permanently "Progressing" even when Kubernetes itself reports it fine.

bash
argocd app resources myapp -o json | jq '.[] | select(.health.status != "Healthy")'
lua
-- Example custom health check (argocd-cm ConfigMap) for a Certificate CRD
-- BUG: this never returns "Healthy" if .status.conditions is empty on first sync
health_status = {}
if obj.status ~= nil and obj.status.conditions ~= nil then
  for i, condition in ipairs(obj.status.conditions) do
    if condition.type == "Ready" and condition.status == "True" then
      health_status.status = "Healthy"
      return health_status
    end
  end
end
health_status.status = "Progressing"    -- Falls through here forever if conditions never populate as expected
return health_status

Fix — add a fallback and test the Lua script against real resource output:

bash
kubectl get certificate mycert -n production -o yaml    # Compare actual status structure to what your Lua expects

Cause 3: PreSync/PostSync Hook Never Completes

bash
kubectl get jobs -n production -l argocd.argoproj.io/hook=PreSync
# NAME              COMPLETIONS   DURATION   AGE
# db-migration-job  0/1           15m        15m    ← stuck

ArgoCD will not mark the application Healthy until PreSync hooks complete successfully — a hung migration job blocks the entire sync indefinitely.

bash
kubectl logs job/db-migration-job -n production
# Check why the job itself is stuck — connection timeout, missing secret, etc.

Fix — terminate the stuck hook and fix the underlying job issue:

bash
kubectl delete job db-migration-job -n production
argocd app sync myapp    # Re-triggers the hook with the fix in place

Add a hook deletion policy so failed hooks don't accumulate and block future syncs:

yaml
metadata:
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookFailed,HookSucceeded

Cause 4: Argo Rollouts Canary Stuck at a Pause Step

If you're using Argo Rollouts instead of plain Deployments, a canary can sit at a manual pause step indefinitely — this is expected behavior, not a bug, but it's easy to forget.

bash
kubectl argo rollouts get rollout myapp -n production
# Status: Paused  ← waiting for manual promotion or an analysis step to complete

Fix — promote manually, or check why an automated AnalysisRun hasn't completed:

bash
kubectl argo rollouts promote myapp -n production
 
# Or check the analysis run if it's supposed to auto-promote
kubectl get analysisrun -n production
kubectl describe analysisrun myapp-abc123 -n production

Cause 5: Resource Field Excluded From Health Check, Sync Never Settles

bash
# ArgoCD sometimes shows OutOfSync -> Synced -> OutOfSync in a loop
# because an operator or admission webhook mutates a field ArgoCD manages
argocd app diff myapp

Fix — ignore fields that a controller legitimately owns and mutates post-apply:

yaml
spec:
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas    # HPA owns this, ArgoCD shouldn't fight it

Diagnostic Checklist

bash
argocd app get myapp                                   # overall status
argocd app resources myapp                              # per-resource health
kubectl get events -n production --sort-by='.lastTimestamp' | tail -20
kubectl get jobs -n production -l argocd.argoproj.io/hook
argocd app diff myapp                                    # what's actually out of sync

More ArgoCD troubleshooting? Read our ArgoCD sync failed unknown field fix and ArgoCD sync stuck unknown fix.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments