In Argo CD, what are resource hooks, which phases can you attach one to, and how would you use them to run a database migration before a new version rolls out?
answer
- annotated manifests, not desired state
- four points around one sync
- migration runs before manifests apply
- phase waits on hook health
- the Job name collides next time
basics
~20 sArgo CD resource hooks are ordinary Kubernetes manifests annotated with argocd.argoproj.io/hook so they run at a chosen point of a sync: PreSync, Sync, PostSync or SyncFail. A schema migration is a Job annotated PreSync, so the sync stops if it fails.
solid answer
~50 sA hook is just a manifest in the same source, marked with `argocd.argoproj.io/hook` so that Argo CD executes it as part of the sync operation instead of treating it as a permanent piece of desired state. The phases are `PreSync` (before any manifests are applied), `Sync` (alongside the normal manifests), `PostSync` (after everything is applied *and* reports Healthy), and `SyncFail` (only when the operation fails); `Skip` also exists, which tells Argo CD not to apply that manifest at all. Argo CD waits on a hook's health before moving to the next phase, so a failing `PreSync` hook aborts the sync and the new manifests are never applied. That is exactly what you want for a schema migration: a `Job` annotated `argocd.argoproj.io/hook: PreSync`, plus `argocd.argoproj.io/hook-delete-policy` so the Job object does not collide with the next run. Helm charts get the same treatment — Argo CD maps `helm.sh/hook` annotations onto its own hook phases.
code
yaml · 16 linesapiVersion: batch/v1
kind: Job
metadata:
name: smoke-test
annotations:
argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: smoke
image: curlimages/curl:8.5.0
args: ["-fsS", "http://checkout.default.svc.cluster.local:8080/healthz"]go deeper
Know that a hook is a normal manifest with an Argo CD annotation, that PreSync runs before the app is updated, and that a database migration is the standard example.
Name all four phases and state the ordering rule: Argo CD waits for a phase's hooks to report Healthy before starting the next one, so a failed PreSync aborts the sync. Mention hook-delete-policy without being asked.
Bring the operational consequences: the old version serves traffic during PreSync so migrations must be backward-compatible, hook objects need a delete policy or a generated name, and PostSync failure does not undo anything.
Own where the boundary sits: which release work belongs in a sync hook at all versus in the application's own startup path, a separate pipeline stage, or a progressive-delivery controller — and what that choice costs you in rollback ability.
## The problem hooks solve A sync is fundamentally "apply this set of manifests". Real deployments need work that is *not* a piece of persistent desired state: migrate the database schema before the new code starts, warm a cache, run a smoke test after the rollout, page someone when the sync fails. Those are one-shot actions bound to the act of deploying. Argo CD models them as **resource hooks**. ## What a hook actually is A hook is an ordinary Kubernetes manifest stored alongside the rest of the Application's source, carrying an annotation: ```yaml apiVersion: batch/v1 kind: Job metadata: name: db-migrate annotations: argocd.argoproj.io/hook: PreSync argocd.argoproj.io/hook-delete-policy: BeforeHookCreation spec: template: spec: restartPolicy: Never containers: - name: migrate image: registry.example.com/app:1.4.2 command: ["/app/migrate", "up"] ``` Any kind can be a hook, but a `Job` is by far the most common because it has a natural notion of success and failure. Because the annotation is present, Argo CD does not treat the object as part of the Application's steady-state desired configuration — it creates it during the sync operation and evaluates it as part of that operation. ## The phases | Phase | When it runs | Typical use | |-------|--------------|-------------| | `PreSync` | Before any of the Application's manifests are applied | Schema migration, backup, pre-flight check | | `Sync` | Together with the ordinary manifests | Complex migrations that need to run beside the app | | `PostSync` | After all manifests are applied and report Healthy | Smoke test, cache warm, deployment notification | | `SyncFail` | Only when the sync operation fails | Cleanup, alerting, compensating action | | `Skip` | Never applied | Excluding a manifest from this Application | The crucial mechanic is that Argo CD **waits for a phase's hooks to become Healthy before it starts the next phase**, using the same health assessment it applies to any other resource: a `Job` is Healthy when it completes successfully and Degraded when it fails. So a failed `PreSync` migration aborts the whole operation and the new Deployment manifest is never applied — the old version keeps serving, which is precisely the ordering you want when the new code depends on new columns. Within a phase, ordering is controlled by the sync-wave annotation, so several hooks in the same phase can be sequenced. ## Hook lifecycle and delete policies Hook objects are real objects in the cluster, and a `Job` with a fixed name cannot be created twice. Left unmanaged, the second sync fails because the object already exists, or worse, the old completed Job is silently left in place and no migration runs. `argocd.argoproj.io/hook-delete-policy` controls this: - `BeforeHookCreation` (the practical default choice) — delete the previous instance immediately before creating the new one, so the most recent run is still available for inspection. - `HookSucceeded` — delete as soon as the hook succeeds, leaving nothing behind on the happy path. - `HookFailed` — delete when the hook fails. You can also generate a fresh name per run (`generateName`), but then you accumulate objects unless you also set a TTL. ## Helm charts Charts already express this idea with `helm.sh/hook`, `helm.sh/hook-weight` and `helm.sh/hook-delete-policy`. Argo CD understands those and maps them onto its own phases — a chart's `pre-install`/`pre-upgrade` hook behaves as a `PreSync` hook. You do not need to rewrite third-party charts to make them work under Argo CD. ## What hooks are not A hook is not a rollback mechanism. If a `PostSync` hook fails, the manifests are already applied and stay applied; Argo CD marks the operation failed and runs any `SyncFail` hooks, but nothing is reverted. And a hook is not a general scheduler — anything that must run on a schedule independent of deployments belongs in a normal CronJob that is part of the desired state, not a hook. ## Interview framing The answer that lands names the four phases *and* the ordering guarantee (phase N's hooks must be Healthy before phase N+1 begins), then immediately volunteers the two operational gotchas: the delete policy, and the fact that a migration must be backward-compatible with the currently-running version, because during a `PreSync` migration the old code is still serving traffic against the new schema.
- Your PreSync migration Job succeeds once, then every later sync fails with "job already exists". What is wrong?The hook Job has a fixed name and no `argocd.argoproj.io/hook-delete-policy`, so the completed object from the previous sync is still in the cluster and blocks creation. Set `BeforeHookCreation` to have Argo CD delete the prior instance just before creating the new one — that also keeps the last run's logs available until the next deploy — or give the Job a `generateName` plus a TTL.
- During a PreSync migration, which version of the application code is serving traffic?The old one. `PreSync` runs before any of the new manifests are applied, so the previous Deployment is untouched and still handling requests against the freshly migrated schema. That is why migrations must be backward-compatible — additive columns, no destructive renames in the same release — otherwise you break production between the migration finishing and the new pods becoming ready.
- How does Argo CD treat a Helm chart that already uses helm.sh/hook annotations?It understands them and maps them onto its own phases, so a chart's `pre-install`/`pre-upgrade` hook behaves as a `PreSync` hook and `post-install`/`post-upgrade` as `PostSync`. Helm's hook weights order hooks within a phase. You can consume upstream charts unmodified; you only add Argo-specific annotations for behaviour the chart does not already express.
saying these in an interview costs you the question
- Thinks hooks are scheduled jobs rather than sync-bound actions
- Says a failed PreSync hook still lets the manifests apply
- Forgets hook objects persist and collide on the next sync
- Believes a failed PostSync hook rolls the deployment back
- Cannot name any phase other than PreSync