What Are Helm Hooks and How Do They Work?
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
- Always set hook-delete-policy — without it, old hook resources accumulate indefinitely
- Use Jobs, not Pods — Jobs provide retry logic via
backoffLimit - Set activeDeadlineSeconds — prevent hooks from hanging forever
- Test hooks with
helm template— verify they render correctly before deploying - Keep hooks idempotent — they may run multiple times during retries
- 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
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.