🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoys3 min read
Share:Tweet

ArgoCD hooks — PreSync, PostSync, SyncFail — are powerful but notoriously hard to debug when they fail. The app stays in Progressing forever, or flips to Degraded with no useful message in the UI.

What a Hook Failure Looks Like

In the ArgoCD UI, you'll see:

Health Status: Degraded
Sync Status: OutOfSync
Message: Job.batch "my-pre-sync-job" is invalid

Or the hook Job just disappears after failure, taking the logs with it.

Why This Happens

The most common causes:

  1. Hook Job fails — pod exits non-zero, ArgoCD marks sync as failed
  2. Hook Job deleted before you can read logsargocd.argoproj.io/hook-delete-policy: HookSucceeded deletes on success, but BeforeHookCreation deletes it before you can even see it fail
  3. Resource quota / namespace limits — Job can't schedule pods
  4. Wrong hook annotation — typo in argocd.argoproj.io/hook value

Step 1: Find the Failed Hook Job

bash
# List all Jobs in the namespace
kubectl get jobs -n your-namespace
 
# Look for failed jobs
kubectl get jobs -n your-namespace --field-selector status.failed=1
 
# Get events
kubectl describe job my-pre-sync-job -n your-namespace

Step 2: Get the Logs Before They Disappear

The hook pod gets deleted after the Job completes. You need to be fast — or change the delete policy.

bash
# While hook is running, grab logs immediately
kubectl logs -n your-namespace -l job-name=my-pre-sync-job --previous
 
# Or watch pods
kubectl get pods -n your-namespace -w | grep hook

To prevent auto-deletion while debugging, temporarily change the hook delete policy:

yaml
metadata:
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookFailed  # only delete if it fails

Or set it to Never while debugging:

yaml
    argocd.argoproj.io/hook-delete-policy: Never

Remove this after you've fixed the issue.

Step 3: Common Hook Fixes

Fix 1: Hook Job exits non-zero

Check the container's exit code:

bash
kubectl describe pod -n your-namespace -l job-name=my-pre-sync-job

Look at Exit Code in the container status. A script returning exit 1 will fail the hook.

In your hook Job spec, make sure your command is correct:

yaml
spec:
  template:
    spec:
      containers:
        - name: hook
          image: bitnami/kubectl:latest
          command: ["/bin/sh", "-c"]
          args:
            - |
              kubectl apply -f /config/migration.yaml || exit 1
      restartPolicy: Never  # must be Never for hook Jobs

Fix 2: ImagePullBackOff in hook pod

bash
kubectl describe pod -n your-namespace <hook-pod-name>

If the image can't be pulled, add imagePullSecrets:

yaml
spec:
  template:
    spec:
      imagePullSecrets:
        - name: ecr-registry-secret

Fix 3: Hook Job can't be created — resource quota

bash
kubectl describe resourcequota -n your-namespace

If you're hitting CPU/memory quota, reduce the hook Job's resource requests:

yaml
resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"
    memory: "256Mi"

Fix 4: ArgoCD not picking up hook annotations

Verify your annotation is on the correct resource:

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync        # ← must be exact
    argocd.argoproj.io/hook-delete-policy: BeforeHookCreation

Valid hook values: PreSync, Sync, PostSync, SyncFail, Skip

Case matters — presync won't work.

Step 5: Retry a Stuck Sync

If your app is stuck in Progressing and the hook has already failed:

bash
# Force a fresh sync
argocd app sync your-app --force
 
# Or terminate the in-progress operation
argocd app terminate-op your-app
argocd app sync your-app

Prevent This in the Future

Add a timeout to your hook Jobs so they don't hang forever:

yaml
spec:
  activeDeadlineSeconds: 120  # fail if not done in 2 min
  backoffLimit: 0             # don't retry on failure

Enable ArgoCD notifications so you get Slack alerts when a sync fails — saves you from discovering it hours later.


Tooling used: ArgoCD 2.x, kubectl, Kubernetes Jobs

If your hooks are database migrations, check out Flyway or Liquibase — they handle migration state properly and work well as ArgoCD PreSync hooks.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

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

Comments