How to Order Argo CD Deployments with Sync Phases, Waves, and Hooks
A deterministic Argo CD rollout using phases, integer waves, health gates, idempotent hooks, selective-sync caveats, and observable failure recovery.
Kubernetes reconciliation is intentionally asynchronous. Argo CD can apply many independent resources without hand-written ordering, but some rollouts have real dependencies: a migration must finish before new application code starts, a CRD must exist before its custom resources, or a smoke test must pass before cleanup. Argo CD models those cases with sync phases, integer waves, resource-kind ordering, health checks, and hooks.
Use ordering only for genuine dependencies
Before adding annotations, ask whether Kubernetes-native readiness can express the relationship. A Deployment waiting for a Service endpoint, a controller reconciling a Custom Resource, or an application retrying a dependency often needs no fixed apply sequence. Excessive waves serialize work and encode a fragile procedural installer inside a declarative repository.
Argo CD processes phases in this broad order: PreSync, Sync, and PostSync; SyncFail runs on failure and PostDelete applies to deletion workflows in supported releases. Within a phase, it selects the lowest wave with out-of-sync or unhealthy resources, applies that wave, then waits for health before advancing.
Assign integer waves deliberately
Set argocd.argoproj.io/sync-wave to a quoted integer. Negative values are valid and useful for prerequisites:
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1"
A small plan is easier to audit than dozens of magic numbers:
-2 namespace-level prerequisites
-1 CRDs and controllers that must become healthy
0 ordinary application resources
1 dependent jobs or consumers
2 post-deployment verification
Argo CD also orders by kind and name, but do not depend on incidental alphabetical order for a business-critical migration. Document why each nonzero wave exists.
Use hooks as retryable Kubernetes resources
A database migration can be a PreSync hook Job:
apiVersion: batch/v1
kind: Job
metadata:
generateName: payments-migrate-
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: registry.example.com/payments/migrator@sha256:...
args: ["migrate", "up"]
Make migrations idempotent or explicitly detect already-applied versions. Pin images by digest, use a least-privilege service account, set deadlines, and emit durable logs. A hook that assumes it runs exactly once is unsafe because operators retry syncs and controllers recover after partial failure.
Choose deletion policy based on forensic needs. Automatically deleting successful Jobs reduces clutter but removes their Kubernetes object; retain logs and migration evidence elsewhere. BeforeHookCreation can replace an existing named hook, while HookFailed removes evidence unless logs are exported first.
Health is the gate between waves
Argo CD advances only when the current wave’s resources are synced and healthy. Built-in health checks understand common Kubernetes kinds; custom resources may require custom health logic. A controller can accept a CR while its status never reaches the state Argo CD expects, deadlocking later waves.
Test degraded, progressing, suspended, and unknown states. A fixed sleep is not a health model. If a CRD’s semantics require a custom check, version that logic with the application and verify it against real status objects.
Know the selective-sync trap
Argo CD documentation notes that hooks do not run during selective sync. An operator syncing only one resource can therefore bypass a migration or verification hook that a full application sync would execute. Restrict selective-sync privileges where hooks protect correctness, or design the application so skipping a hook cannot corrupt state.
Automated sync, pruning, and self-heal also affect recovery. A failed wave leaves later waves unapplied, but already-applied resources may remain. Document whether rollback means a Git revert, a forward fix, a hook retry, or database restoration; Argo CD cannot infer application-level reversibility.
Acceptance-test the complete state machine
Test first install, no-op repeat, ordinary update, hook failure, health timeout, controller restart, partial application, rollback, prune, and selective sync. Capture operation history and Kubernetes events. Alert on applications stuck in a wave, hooks nearing deadlines, and custom resources with unknown health.
Ordering is successful when it is small, deterministic, observable, and safe to retry—not simply when resources appear in the preferred visual order in the Argo CD UI.
Related:
- Kubernetes RBAC Impersonation: Testing Authorization Without Sharing Credentials
- Kubernetes Server-Side Apply: Field Ownership, Conflicts, and managedFields
Sources: