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.
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
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# More detail per resource
kubectl get deployment myapp -n production -o jsonpath='{.status.conditions}' | jq .Cause 1: Deployment Never Reaches Available Replicas
kubectl get pods -n production -l app=myapp
# NAME READY STATUS RESTARTS
# myapp-7f9c-abc12 0/1 ImagePullBackOff 0ArgoCD'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):
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.
argocd app resources myapp -o json | jq '.[] | select(.health.status != "Healthy")'-- 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_statusFix — add a fallback and test the Lua script against real resource output:
kubectl get certificate mycert -n production -o yaml # Compare actual status structure to what your Lua expectsCause 3: PreSync/PostSync Hook Never Completes
kubectl get jobs -n production -l argocd.argoproj.io/hook=PreSync
# NAME COMPLETIONS DURATION AGE
# db-migration-job 0/1 15m 15m ← stuckArgoCD will not mark the application Healthy until PreSync hooks complete successfully — a hung migration job blocks the entire sync indefinitely.
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:
kubectl delete job db-migration-job -n production
argocd app sync myapp # Re-triggers the hook with the fix in placeAdd a hook deletion policy so failed hooks don't accumulate and block future syncs:
metadata:
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookFailed,HookSucceededCause 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.
kubectl argo rollouts get rollout myapp -n production
# Status: Paused ← waiting for manual promotion or an analysis step to completeFix — promote manually, or check why an automated AnalysisRun hasn't completed:
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 productionCause 5: Resource Field Excluded From Health Check, Sync Never Settles
# ArgoCD sometimes shows OutOfSync -> Synced -> OutOfSync in a loop
# because an operator or admission webhook mutates a field ArgoCD manages
argocd app diff myappFix — ignore fields that a controller legitimately owns and mutates post-apply:
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA owns this, ArgoCD shouldn't fight itDiagnostic Checklist
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 syncMore 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
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
ArgoCD App of Apps Not Syncing — Every Fix (2026)
Your ArgoCD App of Apps pattern stopped syncing. Child apps aren't created, parent shows OutOfSync, or sync is stuck. Here are every cause and the exact fix.
ArgoCD Resource Hook Failed: How to Debug and Fix It
ArgoCD PreSync or PostSync hooks failing silently? Here's how to find the real error, fix hook job issues, and stop your deployments from getting stuck.
ArgoCD Sync Failed: Unknown Field and Validation Errors — Fix
ArgoCD sync failing with 'unknown field', 'strict decoding error', or 'field is immutable'? Here are the exact causes and kubectl/ArgoCD commands to fix each one without recreating resources.