What Are Helm Hooks and How Do They Work?

intermediate|helmdevopssrebackend developerCKACKAD
TL;DR

Helm hooks are special resources annotated to run at specific points in a release lifecycle — before install, after install, before upgrade, after upgrade, before delete, and after delete. They are commonly used for database migrations, backups, and validation.

Detailed Answer

Helm hooks let you execute actions at specific points in a release lifecycle. They are Kubernetes resources (usually Jobs or Pods) with special annotations that tell Helm when to create them.

Hook Lifecycle Points

| Hook | When It Runs | |------|-------------| | pre-install | Before any release resources are created | | post-install | After all release resources are created | | pre-upgrade | Before any upgrade resources are updated | | post-upgrade | After all upgrade resources are updated | | pre-delete | Before any release resources are deleted | | post-delete | After all release resources are deleted | | pre-rollback | Before a rollback occurs | | post-rollback | After a rollback completes | | test | When helm test is invoked |

Basic Hook Example: Database Migration

# templates/migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "my-chart.fullname" . }}-migrate
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "0"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
  backoffLimit: 3
  template:
    spec:
      containers:
        - name: migrate
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          command: ["./migrate", "--up"]
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: {{ include "my-chart.fullname" . }}-db
                  key: url
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
      restartPolicy: Never

This Job runs before both install and upgrade. It runs database migrations before the new application version starts.

Hook Execution Flow

helm upgrade my-release ./my-chart

1. Helm renders all templates
2. Helm identifies hook resources (by annotation)
3. Helm sorts hooks by weight (ascending)
4. Pre-upgrade hooks execute:
   a. Weight -5: Backup Job runs → completes
   b. Weight  0: Migration Job runs → completes
   c. Weight  5: Validation Job runs → completes
5. Regular resources are applied (Deployments, Services, etc.)
6. Helm waits for readiness
7. Post-upgrade hooks execute
8. Release is marked as deployed

Hook Weight (Execution Order)

# Backup first (lower weight = runs first)
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-backup
  annotations:
    "helm.sh/hook": pre-upgrade
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
  template:
    spec:
      containers:
        - name: backup
          image: backup-tool:1.0
          command: ["./backup", "--database", "mydb"]
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
      restartPolicy: Never
---
# Then migrate (higher weight = runs second)
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-migrate
  annotations:
    "helm.sh/hook": pre-upgrade
    "helm.sh/hook-weight": "0"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          command: ["./migrate"]
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
      restartPolicy: Never

Hook Delete Policies

| Policy | Behavior | |--------|----------| | before-hook-creation | Delete the previous hook resource before creating a new one | | hook-succeeded | Delete the hook resource after it succeeds | | hook-failed | Delete the hook resource after it fails |

Best practice: Use before-hook-creation,hook-succeeded for most hooks. This cleans up successful runs and replaces old hooks, but leaves failed hooks for debugging.

Post-Install Notification

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-notify
  annotations:
    "helm.sh/hook": post-install,post-upgrade
    "helm.sh/hook-weight": "10"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    spec:
      containers:
        - name: notify
          image: curlimages/curl:latest
          command:
            - curl
            - -X
            - POST
            - -H
            - "Content-Type: application/json"
            - -d
            - '{"text":"Release {{ .Release.Name }} deployed version {{ .Chart.AppVersion }}"}'
            - "$(SLACK_WEBHOOK_URL)"
          envFrom:
            - secretRef:
                name: slack-webhook
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
      restartPolicy: Never

Pre-Delete Backup

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-final-backup
  annotations:
    "helm.sh/hook": pre-delete
    "helm.sh/hook-weight": "0"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    spec:
      containers:
        - name: backup
          image: backup-tool:1.0
          command: ["./backup", "--full", "--tag", "pre-delete"]
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
      restartPolicy: Never

Hook Timeouts

Hooks do not have a built-in timeout, but you can control it:

# Set a timeout for the entire install/upgrade
helm install my-release ./my-chart --timeout 10m

# The timeout applies to waiting for hooks to complete

For Jobs, set activeDeadlineSeconds:

spec:
  activeDeadlineSeconds: 300  # Fail after 5 minutes
  backoffLimit: 3

Debugging Hook Failures

# Check hook Job status
kubectl get jobs -l "app.kubernetes.io/instance=my-release"

# View hook Pod logs
kubectl logs job/my-release-migrate

# List all hooks for a release
helm get hooks my-release

# Check what happened during the release
helm history my-release

Common Patterns

| Pattern | Hook Stage | Example | |---------|-----------|---------| | Database migration | pre-install, pre-upgrade | Run schema changes before app starts | | Data backup | pre-upgrade, pre-delete | Snapshot data before risky operations | | Smoke test | post-install, post-upgrade | Verify the release is healthy | | Notification | post-install, post-upgrade | Notify Slack or PagerDuty | | Cache warming | post-install | Pre-populate caches before traffic arrives | | Cleanup | pre-delete | Remove external resources (DNS, LB) |

Best Practices

  1. Always set hook-delete-policy — without it, old hook resources accumulate indefinitely
  2. Use Jobs, not Pods — Jobs provide retry logic via backoffLimit
  3. Set activeDeadlineSeconds — prevent hooks from hanging forever
  4. Test hooks with helm template — verify they render correctly before deploying
  5. Keep hooks idempotent — they may run multiple times during retries
  6. Use hook-weight for ordering — do not rely on filename ordering

Why Interviewers Ask This

Hooks are essential for managing side effects during deployments like schema migrations and data backups. Understanding them shows you can implement reliable release workflows.

Common Follow-Up Questions

What happens if a pre-install hook fails?
The entire release is marked as failed and the install is aborted. Resources from the failed hook remain for debugging unless a delete policy is set.
What is the difference between hook-delete-policy and a regular resource?
hook-delete-policy controls when hook resources are cleaned up: before-hook-creation (delete old before running new), hook-succeeded (delete after success), or hook-failed (delete after failure).
Can you set the order of multiple hooks at the same stage?
Yes — use the helm.sh/hook-weight annotation. Lower weights run first. Hooks with the same weight run in no guaranteed order.

Key Takeaways

  • Hooks run at specific lifecycle points: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete.
  • Use hook-weight to control execution order when multiple hooks run at the same stage.
  • Always set hook-delete-policy to avoid accumulating completed hook resources.

Related Questions

You Might Also Like