Inside one sync Argo CD applies objects in a sensible order: Namespaces and CRDs first, then ConfigMaps and Secrets, then workloads. When you need your own order, use waves and hooks.
Sync phases and waves
A sync runs in phases: PreSync → Sync → PostSync (and SyncFail if it fails). Inside each phase, objects run in waves, lowest first; Argo CD waits until a wave is healthy before the next one.
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1" # default is 0; negative runs earlier
| Wave | Example |
|---|---|
-2 |
Namespace, ServiceAccount, external-secrets ExternalSecret |
-1 |
Database StatefulSet |
0 |
API Deployment, Service |
1 |
Ingress, smoke-test Job |
Hooks
A hook is an object (usually a Job) that runs at a phase instead of being kept in sync:
apiVersion: batch/v1
kind: Job
metadata:
name: shop-db-migrate
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: registry.example.com/shop-api:1.4.3
command: ["npm", "run", "migrate"]
| Hook | Runs | Typical use |
|---|---|---|
PreSync |
Before anything is applied | DB schema migration, backup |
Sync |
With the normal objects | Complex ordering not covered by waves |
PostSync |
After all objects are healthy | Smoke test, notify, warm cache |
SyncFail |
When the sync failed | Clean up, alert |
Skip |
Never applied | Objects only for other tools |
If a PreSync hook fails, the sync stops and the new version is never rolled out — the migration acts as a gate.
Delete policies
hook-delete-policy |
Effect |
|---|---|
BeforeHookCreation (default) |
Delete the old hook object just before creating the new one |
HookSucceeded |
Delete it after it succeeds |
HookFailed |
Delete it after it fails |
Jobs have immutable fields, so without a delete policy (or generateName) the next sync cannot create the same Job again.
Helm hooks
Helm charts with helm.sh/hook: pre-install,pre-upgrade are mapped to PreSync, post-install to PostSync, and hook-weight to waves. Note that pre-install and pre-upgrade both become PreSync, so they run on every sync.
Make hooks safe
- Migrations must be idempotent and backwards compatible: the old Pods keep running against the new schema until the rollout finishes.
- Give Jobs a
backoffLimitand anactiveDeadlineSeconds, so a stuck hook does not block syncs forever. - Use
argocd app terminate-op shop-api-prodto stop a running sync that hangs on a hook.