Sync waves and phases allow you to control the order in which Argo CD applies resources during a sync operation. This is essential for dependencies like databases before applications, or configuration before deployments.
Understanding Phases
Argo CD supports multiple sync phases that execute in order:
Phase Description PreSyncExecutes before applying manifests SyncExecutes with the main application manifests SkipSkips resource application entirely PostSyncExecutes after successful sync and health checks SyncFailExecutes when sync fails PreDeleteExecutes before Application deletion PostDeleteExecutes after Application deletion
How Phases Work
During a sync operation, Argo CD processes phases sequentially:
PreSync hooks run first. If any fail, sync stops
Sync hooks and regular resources run. If any fail, SyncFail hooks run
PostSync hooks run after all resources are healthy
Hooks do not run during selective sync operations.
Configuring Phases
Assign resources to phases using the hook annotation:
apiVersion : batch/v1
kind : Job
metadata :
name : db-migration
annotations :
argocd.argoproj.io/hook : PreSync
spec :
template :
spec :
containers :
- name : migrate
image : migrate-tool:latest
command : [ "migrate" , "up" ]
restartPolicy : Never
Multiple hooks can be specified as comma-separated: PreSync,PostSync
Understanding Sync Waves
Sync waves provide fine-grained ordering within phases using integer values. Resources are applied from lowest to highest wave number.
Key concepts:
Default wave: 0
Waves can be negative (e.g., -5 runs before 0)
2-second delay between waves (configurable via ARGOCD_SYNC_WAVE_DELAY)
Argo CD waits for each wave to be healthy before proceeding
Configuring Sync Waves
Use the argocd.argoproj.io/sync-wave annotation:
apiVersion : v1
kind : Namespace
metadata :
name : my-app
annotations :
argocd.argoproj.io/sync-wave : "-5"
---
apiVersion : v1
kind : ConfigMap
metadata :
name : app-config
annotations :
argocd.argoproj.io/sync-wave : "0"
data :
setting : value
---
apiVersion : apps/v1
kind : Deployment
metadata :
name : my-app
annotations :
argocd.argoproj.io/sync-wave : "1"
spec :
replicas : 3
# ... deployment spec
Sync Order Priority
Argo CD determines resource order using this precedence:
Phase (PreSync → Sync → PostSync)
Wave (lowest to highest number)
Kind (Namespaces first, then other resources, then custom resources)
Name (alphabetical)
Combining Phases and Waves
Use phases for coarse-grained ordering and waves for precise control:
# Wave -1: Database migration (PreSync)
apiVersion : batch/v1
kind : Job
metadata :
name : db-migrate
annotations :
argocd.argoproj.io/hook : PreSync
argocd.argoproj.io/sync-wave : "-1"
argocd.argoproj.io/hook-delete-policy : HookSucceeded
spec :
template :
spec :
containers :
- name : migrate
image : postgres:13
command : [ "psql" , "-f" , "migrations.sql" ]
restartPolicy : Never
---
# Wave 0: ConfigMaps and Secrets
apiVersion : v1
kind : ConfigMap
metadata :
name : app-config
annotations :
argocd.argoproj.io/sync-wave : "0"
data :
database.url : postgres://db:5432
---
# Wave 1: Deployments
apiVersion : apps/v1
kind : Deployment
metadata :
name : app
annotations :
argocd.argoproj.io/sync-wave : "1"
spec :
replicas : 3
template :
spec :
containers :
- name : app
image : my-app:v1.0.0
---
# Wave 2: Services and Ingress
apiVersion : v1
kind : Service
metadata :
name : app-service
annotations :
argocd.argoproj.io/sync-wave : "2"
spec :
selector :
app : my-app
ports :
- port : 80
Hook Lifecycle Management
Control hook cleanup with delete policies:
metadata :
annotations :
argocd.argoproj.io/hook : PostSync
argocd.argoproj.io/hook-delete-policy : HookSucceeded
Policy Description HookSucceededDelete after successful completion HookFailedDelete after failure BeforeHookCreationDelete existing hook before creating new one (default)
If no delete policy is specified, Argo CD defaults to BeforeHookCreation.
Common Examples
Database Initialization
Run database setup before application deployment:
apiVersion : batch/v1
kind : Job
metadata :
name : db-init
annotations :
argocd.argoproj.io/hook : PreSync
argocd.argoproj.io/hook-delete-policy : HookSucceeded
argocd.argoproj.io/sync-wave : "-1"
spec :
ttlSecondsAfterFinished : 360
template :
spec :
containers :
- name : init-db
image : postgres:13
env :
- name : PGPASSWORD
value : admin
command :
- psql
- "-h=postgres-db"
- "-U postgres"
- "-f init.sql"
restartPolicy : Never
backoffLimit : 1
Smoke Tests (PostSync)
Verify deployment health after sync:
apiVersion : batch/v1
kind : Job
metadata :
name : smoke-test
annotations :
argocd.argoproj.io/hook : PostSync
argocd.argoproj.io/hook-delete-policy : HookSucceeded
spec :
template :
spec :
containers :
- name : test
image : curlimages/curl
command :
- curl
- "--fail"
- "http://my-app/health"
restartPolicy : Never
backoffLimit : 3
Slack Notification
Send notification after successful sync:
apiVersion : batch/v1
kind : Job
metadata :
generateName : slack-notification-
annotations :
argocd.argoproj.io/hook : PostSync
argocd.argoproj.io/hook-delete-policy : HookSucceeded
spec :
template :
spec :
containers :
- name : notify
image : curlimages/curl
command :
- curl
- "-X"
- POST
- "--data-urlencode"
- > -
payload={"channel": "#deployments", "username": "ArgoCD",
"text": "App sync succeeded", "icon_emoji": ":rocket:"}
- "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
restartPolicy : Never
backoffLimit : 2
Skip Helm Hook
Prevent Helm-generated hooks from running:
ingress-nginx :
controller :
admissionWebhooks :
annotations :
argocd.argoproj.io/hook : Skip
PreDelete and PostDelete Hooks
PreDelete Hook
Run cleanup before application deletion:
apiVersion : batch/v1
kind : Job
metadata :
name : pre-delete-cleanup
annotations :
argocd.argoproj.io/hook : PreDelete
argocd.argoproj.io/hook-delete-policy : HookSucceeded
spec :
template :
spec :
containers :
- name : cleanup
image : cleanup-tool:latest
command : [ "cleanup.sh" ]
restartPolicy : Never
Behavior:
Runs only during Application deletion (not during normal sync with pruning)
Application deletion blocks until hooks complete successfully
If hook fails, Application enters DeletionError state
Failure Handling:
Fix the hook in Git; Argo CD retries on next reconciliation
Or manually delete the failing hook resource to proceed
PostDelete Hook
Run tasks after all resources are deleted:
apiVersion : batch/v1
kind : Job
metadata :
name : post-delete-notify
annotations :
argocd.argoproj.io/hook : PostDelete
argocd.argoproj.io/hook-delete-policy : HookSucceeded
spec :
template :
spec :
containers :
- name : notify
image : notification-tool:latest
command : [ "send-notification.sh" ]
restartPolicy : Never
Behavior:
Runs after all Application resources are deleted
Application CR remains until hooks complete
Useful for external cleanup, notifications, or audit logs
Advanced Patterns
Multi-Tier Application
Deploy infrastructure, then application, then monitoring:
# Wave -5: Namespace
apiVersion : v1
kind : Namespace
metadata :
name : my-app
annotations :
argocd.argoproj.io/sync-wave : "-5"
---
# Wave -3: Database StatefulSet
apiVersion : apps/v1
kind : StatefulSet
metadata :
name : postgres
annotations :
argocd.argoproj.io/sync-wave : "-3"
spec :
serviceName : postgres
replicas : 3
# ... statefulset spec
---
# Wave -2: Database migration
apiVersion : batch/v1
kind : Job
metadata :
name : db-migrate
annotations :
argocd.argoproj.io/hook : PreSync
argocd.argoproj.io/sync-wave : "-2"
argocd.argoproj.io/hook-delete-policy : HookSucceeded
# ... job spec
---
# Wave 0: Application ConfigMap
apiVersion : v1
kind : ConfigMap
metadata :
name : app-config
annotations :
argocd.argoproj.io/sync-wave : "0"
---
# Wave 1: Application Deployment
apiVersion : apps/v1
kind : Deployment
metadata :
name : app
annotations :
argocd.argoproj.io/sync-wave : "1"
# ... deployment spec
---
# Wave 5: Monitoring ServiceMonitor
apiVersion : monitoring.coreos.com/v1
kind : ServiceMonitor
metadata :
name : app-metrics
annotations :
argocd.argoproj.io/sync-wave : "5"
# ... servicemonitor spec
Blue-Green Deployment
Deploy new version, test, then switch traffic:
# Wave 1: New (green) deployment
apiVersion : apps/v1
kind : Deployment
metadata :
name : app-green
annotations :
argocd.argoproj.io/sync-wave : "1"
spec :
selector :
matchLabels :
app : my-app
version : green
# ... deployment spec
---
# Wave 2: Test green deployment
apiVersion : batch/v1
kind : Job
metadata :
name : test-green
annotations :
argocd.argoproj.io/hook : PostSync
argocd.argoproj.io/sync-wave : "2"
spec :
# ... test job spec
---
# Wave 3: Switch service to green
apiVersion : v1
kind : Service
metadata :
name : app-service
annotations :
argocd.argoproj.io/sync-wave : "3"
spec :
selector :
app : my-app
version : green
# ... service spec
Troubleshooting
Resources Not Syncing in Order
Verify annotations are correct:
kubectl get < resourc e > -o yaml | grep sync-wave
Check if resources are in different phases
Ensure wave values are strings: "0", not 0
Wave Stuck in Progressing
Check if resources in the wave are healthy
Review resource events: kubectl describe <resource>
Check hook job logs: kubectl logs job/<job-name>
Sync Too Slow
Reduce wave delay:
# In argocd-application-controller deployment
env:
- name: ARGOCD_SYNC_WAVE_DELAY
value: "1" # 1 second instead of default 2
Best Practices
Use Negative Waves Reserve negative waves for infrastructure (namespaces, CRDs, databases)
Group by Dependency Assign the same wave to resources without dependencies
Test Hooks Separately Test hooks manually before adding to production
Clean Up Hooks Always use delete policies to avoid resource buildup
Be cautious with PreDelete hooks - failed hooks will prevent Application deletion.
Next Steps
Resource Hooks Deep dive into hook types and patterns
Sync Options Configure sync behavior
Health Checks Ensure resources are healthy before proceeding
Creating Apps Learn how to create applications