> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/argoproj/argo-cd/llms.txt
> Use this file to discover all available pages before exploring further.

# Resource Hooks

> Execute jobs and tasks at specific points in the sync lifecycle with resource hooks

Resource hooks allow you to run Kubernetes Jobs, Pods, or other resources at specific points during the sync lifecycle. They're essential for tasks like database migrations, smoke tests, and notifications.

## Available Hook Types

| Hook         | When It Runs                                  | Use Case                                    |
| ------------ | --------------------------------------------- | ------------------------------------------- |
| `PreSync`    | Before applying manifests                     | Database migrations, pre-flight checks      |
| `Sync`       | With manifests, after PreSync succeeds        | Additional resources that sync with the app |
| `Skip`       | Never (resource is ignored)                   | Disable specific resources                  |
| `PostSync`   | After sync succeeds and resources are healthy | Smoke tests, notifications                  |
| `SyncFail`   | When sync operation fails                     | Cleanup, rollback, failure notifications    |
| `PreDelete`  | Before Application deletion                   | Backup data, external cleanup               |
| `PostDelete` | After Application deletion                    | Remove external resources, audit logs       |

## Basic Hook Configuration

Add the hook annotation to any Kubernetes resource:

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: my-hook
  annotations:
    argocd.argoproj.io/hook: PreSync
spec:
  template:
    spec:
      containers:
      - name: hook
        image: my-image:latest
        command: ["do-something.sh"]
      restartPolicy: Never
```

<Note>
  Hooks can be any Kubernetes resource, but Jobs and Pods are most common.
</Note>

## PreSync Hooks

Run before the main sync operation. Ideal for migrations and prerequisites.

### Database Migration Example

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
    argocd.argoproj.io/sync-wave: "-1"
spec:
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      containers:
      - name: migrate
        image: migrate/migrate:v4
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
        command:
        - migrate
        - "-path=/migrations"
        - "-database=$(DATABASE_URL)"
        - up
      restartPolicy: Never
  backoffLimit: 3
```

### Pre-Flight Check Example

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: preflight-check
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: check
        image: busybox:1.36
        command:
        - sh
        - -c
        - |
          # Check if database is ready
          until nc -z postgres-service 5432; do
            echo "Waiting for database..."
            sleep 2
          done
          echo "Database is ready"
      restartPolicy: Never
```

<Warning>
  If a PreSync hook fails, the entire sync operation stops. Design hooks to be idempotent and have appropriate retry logic.
</Warning>

## Sync Hooks

Run at the same time as regular manifests, after PreSync hooks succeed.

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: sync-job
  annotations:
    argocd.argoproj.io/hook: Sync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: sync-task
        image: my-sync-tool:latest
        command: ["sync-data.sh"]
      restartPolicy: Never
```

## PostSync Hooks

Run after all resources are synced and healthy. Perfect for validation and notifications.

### Smoke Test Example

```yaml theme={null}
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:8.0.0
        command:
        - sh
        - -c
        - |
          # Wait for service to be ready
          sleep 10
          
          # Test health endpoint
          if curl --fail --silent http://my-app-service/health; then
            echo "Health check passed"
            exit 0
          else
            echo "Health check failed"
            exit 1
          fi
      restartPolicy: Never
  backoffLimit: 5
```

### Slack Notification Example

```yaml theme={null}
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:8.0.0
        env:
        - name: SLACK_WEBHOOK
          valueFrom:
            secretKeyRef:
              name: slack-webhook
              key: url
        command:
        - sh
        - -c
        - |
          curl -X POST "$SLACK_WEBHOOK" \
            -H 'Content-Type: application/json' \
            -d '{
              "channel": "#deployments",
              "username": "ArgoCD",
              "text": "✅ Application synced successfully",
              "icon_emoji": ":rocket:"
            }'
      restartPolicy: Never
  backoffLimit: 2
```

### Data Seeding Example

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: seed-data
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: seed
        image: postgres:15
        env:
        - name: PGHOST
          value: postgres-service
        - name: PGDATABASE
          value: myapp
        - name: PGUSER
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: PGPASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
        command:
        - psql
        - "-c"
        - "INSERT INTO users (name, email) VALUES ('admin', 'admin@example.com') ON CONFLICT DO NOTHING;"
      restartPolicy: Never
```

## SyncFail Hooks

Run when the sync operation fails. Useful for cleanup and notifications.

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: sync-failure-alert
  annotations:
    argocd.argoproj.io/hook: SyncFail
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: alert
        image: curlimages/curl:8.0.0
        env:
        - name: PAGERDUTY_KEY
          valueFrom:
            secretKeyRef:
              name: pagerduty
              key: api-key
        command:
        - sh
        - -c
        - |
          curl -X POST https://events.pagerduty.com/v2/enqueue \
            -H 'Content-Type: application/json' \
            -d '{
              "routing_key": "'$PAGERDUTY_KEY'",
              "event_action": "trigger",
              "payload": {
                "summary": "ArgoCD sync failed",
                "severity": "error",
                "source": "argocd"
              }
            }'
      restartPolicy: Never
```

<Info>
  SyncFail hooks run even if PreSync or Sync hooks fail. If SyncFail hooks fail, Argo CD marks the operation as failed but takes no special action.
</Info>

## PreDelete Hooks

Run before Application deletion. Only triggers on Application deletion, not during normal sync with pruning.

### Database Backup Example

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: backup-database
  annotations:
    argocd.argoproj.io/hook: PreDelete
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: backup
        image: postgres:15
        env:
        - name: PGHOST
          value: postgres-service
        - name: BACKUP_BUCKET
          value: s3://backups/myapp
        command:
        - sh
        - -c
        - |
          pg_dump -Fc myapp > /tmp/backup.dump
          aws s3 cp /tmp/backup.dump $BACKUP_BUCKET/backup-$(date +%Y%m%d-%H%M%S).dump
      restartPolicy: Never
  backoffLimit: 3
```

### External Resource Cleanup

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: cleanup-external-resources
  annotations:
    argocd.argoproj.io/hook: PreDelete
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: cleanup
        image: amazon/aws-cli:2.13.0
        env:
        - name: APP_NAME
          value: my-app
        command:
        - sh
        - -c
        - |
          # Delete S3 buckets
          aws s3 rb s3://$APP_NAME-storage --force
          
          # Delete RDS instance
          aws rds delete-db-instance \
            --db-instance-identifier $APP_NAME-db \
            --skip-final-snapshot
      restartPolicy: Never
```

<Warning>
  If a PreDelete hook fails, Application deletion is blocked. The Application enters a `DeletionError` state and resources remain in the cluster.
</Warning>

**Recovering from failed PreDelete hooks:**

1. Fix the hook in Git and Argo CD will retry on next reconciliation
2. Or manually delete the failing hook resource: `kubectl delete job <hook-name>`

## PostDelete Hooks

Run after all Application resources are deleted. Available in Argo CD v2.10+.

### Audit Log Example

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: audit-deletion
  annotations:
    argocd.argoproj.io/hook: PostDelete
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: audit
        image: curlimages/curl:8.0.0
        env:
        - name: AUDIT_API
          value: https://audit.example.com/api/events
        command:
        - sh
        - -c
        - |
          curl -X POST "$AUDIT_API" \
            -H 'Content-Type: application/json' \
            -d '{
              "event": "application_deleted",
              "app": "my-app",
              "timestamp": "'$(date -Iseconds)'"
            }'
      restartPolicy: Never
```

## Skip Hook

Prevent resources from being applied:

```yaml theme={null}
apiVersion: v1
kind: Service
metadata:
  name: temp-service
  annotations:
    argocd.argoproj.io/hook: Skip
spec:
  # This service will never be applied
  selector:
    app: my-app
  ports:
  - port: 80
```

<Info>
  Useful for disabling Helm hooks that conflict with Argo CD, such as ingress-nginx admission webhooks.
</Info>

## Hook Deletion Policies

Control when hooks are cleaned up:

```yaml theme={null}
metadata:
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
```

| Policy               | Behavior                                               |
| -------------------- | ------------------------------------------------------ |
| `HookSucceeded`      | Delete after successful completion                     |
| `HookFailed`         | Delete after failure                                   |
| `BeforeHookCreation` | Delete existing hook before creating new one (default) |

<Note>
  If no policy is specified, `BeforeHookCreation` is used by default.
</Note>

### Combining Deletion Policies

```yaml theme={null}
metadata:
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded,HookFailed
```

This deletes the hook whether it succeeds or fails.

## Combining Hooks with Sync Waves

Use sync waves to order hooks within the same phase:

```yaml theme={null}
# Run first
apiVersion: batch/v1
kind: Job
metadata:
  name: db-backup
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/sync-wave: "-2"
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
# ... job spec
---
# Run second
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/sync-wave: "-1"
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
# ... job spec
```

## Multiple Hooks

A resource can have multiple hook types:

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: multi-hook
  annotations:
    argocd.argoproj.io/hook: PreSync,PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  # This job runs both before and after sync
  template:
    spec:
      containers:
      - name: task
        image: my-image:latest
      restartPolicy: Never
```

## Advanced Patterns

### Conditional Hook with Init Container

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: conditional-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      initContainers:
      - name: check-version
        image: postgres:15
        command:
        - sh
        - -c
        - |
          # Check if migration is needed
          VERSION=$(psql -tAc "SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1")
          if [ "$VERSION" -ge "20240101" ]; then
            echo "Already migrated"
            exit 0
          fi
      containers:
      - name: migrate
        image: migrate/migrate:v4
        command: ["migrate", "up"]
      restartPolicy: Never
```

### Hook with Service Account

For hooks that need Kubernetes API access:

```yaml theme={null}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: hook-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: hook-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: hook-rb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: hook-role
subjects:
- kind: ServiceAccount
  name: hook-sa
---
apiVersion: batch/v1
kind: Job
metadata:
  name: k8s-api-hook
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      serviceAccountName: hook-sa
      containers:
      - name: kubectl
        image: bitnami/kubectl:latest
        command:
        - kubectl
        - get
        - pods
      restartPolicy: Never
```

## Hook Best Practices

<CardGroup cols={2}>
  <Card title="Idempotent Hooks" icon="arrows-rotate">
    Design hooks to be safely re-runnable in case of failures
  </Card>

  <Card title="Set Timeouts" icon="clock">
    Use `activeDeadlineSeconds` to prevent hung jobs
  </Card>

  <Card title="Appropriate Backoff" icon="repeat">
    Set reasonable `backoffLimit` for retries
  </Card>

  <Card title="Clean Up" icon="broom">
    Always use deletion policies to avoid resource accumulation
  </Card>
</CardGroup>

### Recommended Job Configuration

```yaml theme={null}
apiVersion: batch/v1
kind: Job
metadata:
  name: well-configured-hook
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  # Delete job 1 hour after completion
  ttlSecondsAfterFinished: 3600
  
  # Retry up to 3 times
  backoffLimit: 3
  
  template:
    spec:
      # Job timeout: 5 minutes
      activeDeadlineSeconds: 300
      
      containers:
      - name: hook
        image: my-image:latest
        command: ["do-task.sh"]
        
        # Container resource limits
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
      
      restartPolicy: Never
```

## Troubleshooting

### Hook Not Running

1. Verify annotation syntax:
   ```bash theme={null}
   kubectl get job my-hook -o yaml | grep argocd
   ```

2. Check hook status in Argo CD UI or CLI:
   ```bash theme={null}
   argocd app get my-app --show-operation
   ```

3. Ensure hook is in the correct phase

### Hook Fails Continuously

1. Check job logs:
   ```bash theme={null}
   kubectl logs job/my-hook
   ```

2. Describe the job:
   ```bash theme={null}
   kubectl describe job my-hook
   ```

3. Verify environment variables and secrets

### Sync Blocked by Failed Hook

```bash theme={null}
# Manually delete the hook to unblock sync
kubectl delete job my-hook

# Or fix the hook in Git and Argo CD will retry
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Sync Waves" icon="wave-pulse" href="/applications/sync-waves">
    Combine hooks with waves for precise control
  </Card>

  <Card title="Sync Options" icon="sliders" href="/applications/sync-options">
    Configure additional sync behavior
  </Card>

  <Card title="Health Checks" icon="heart-pulse" href="/applications/health-checks">
    Ensure resources are healthy
  </Card>

  <Card title="Creating Apps" icon="plus" href="/applications/creating-apps">
    Learn how to create applications
  </Card>
</CardGroup>
