Lifecycle Hooks and Chart Tests
Run one-off work around an install or upgrade, and write a test that verifies a release actually works.
Most of what a chart does is declare resources that stay running. Sometimes you need something that runs once at a specific moment, a database migration before an upgrade, a cleanup job after an uninstall, or a check that the release actually works once it’s up. Helm covers both with hooks and chart tests.
What a hook is
A hook is an ordinary Kubernetes resource, most often a Job, annotated to tell Helm when to run it relative to an install, upgrade, rollback, or uninstall:
# templates/hooks/pre-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Values.appName }}-pre-install
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "0"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: myorg/webapp-migrate:1.0
command: ["./run-migrations.sh"]
The helm.sh/hook annotation is what makes this a hook rather than a normal resource Helm tracks as part of the release. The common hook points are pre-install, post-install, pre-upgrade, post-upgrade, pre-rollback, post-rollback, pre-delete, and post-delete; the full list, including a couple of test-related ones, is in the Helm hooks documentation.
How Helm sequences a hook
For a pre-install hook:
- Helm renders the hook resource and creates it in the cluster, ahead of anything else in the chart.
- Helm waits for it to reach a ready or completed state (a
Jobrunning to completion counts). - Only then does Helm render and apply the rest of the chart’s resources.
post-install reverses step 1 and 3: the chart’s regular resources go in first, then the hook runs. A pre-upgrade hook that fails stops the upgrade, which is exactly the point of running a migration there rather than hoping it succeeds after the fact.
Hook weight and delete policy
helm.sh/hook-weight orders multiple hooks at the same point, lower numbers run first, as plain strings ("0", "5"), and hooks without a weight default to 0. helm.sh/hook-delete-policy controls cleanup, since a completed Job otherwise lingers in the cluster indefinitely:
before-hook-creation(the default): delete the previous hook resource before creating a new one, so retries don’t collide with a leftover Job of the same name.hook-succeeded: delete the resource once it completes successfully.hook-failed: delete it if it fails, useful when you’d rather clean up than leave a failed Job around for inspection; omit it if you want tokubectl logsa failure before it’s removed.
A pre-install,hook-succeeded combination, for instance, means the hook runs once, succeeds, and cleans itself up, leaving no trace beyond your Job’s own output; add hook-failed too if you don’t want a failed attempt hanging around either.
Try it
kubectl get jobs -w
In another terminal:
helm install web ./webapp
Watching the first terminal, you should see the hook Job appear, run, complete, and (depending on its delete policy) disappear, all before the chart’s Deployment shows up. This is the easiest way to actually see the sequencing rather than take it on faith.
Chart tests: verify a release after it’s up
A chart test is a Job that runs on demand, after a release exists, to check that it’s actually working:
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Values.appName }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox:1.36
command: ["wget", "-qO-", "{{ .Values.appName }}"]
restartPolicy: Never
helm install web ./webapp
helm test web
helm test runs every resource annotated helm.sh/hook: test in the release and reports pass or fail based on each Pod’s exit status; a wget that gets a response exits 0, one that can’t connect doesn’t. Unlike install/upgrade hooks, test resources aren’t deleted automatically by default, run helm test --logs to see their output inline, or clean them up yourself between runs.
When to reach for each
- Use a hook when something needs to happen exactly once, at a specific point in a release’s lifecycle, and the rest of the deploy should wait on or react to it: schema migrations before an upgrade, notifying an external system after install, cleaning up external resources before an uninstall.
- Use a chart test for anything you want to re-run on demand against an already-running release, most often as a smoke test after
helm installorhelm upgradein a CI pipeline, without it being part of the deploy sequence itself.
Both are ordinary Kubernetes resources underneath the annotations, so anything you already know about debugging a Job or a Pod, kubectl describe, kubectl logs, applies directly.
Popular Guides
-
Helm Chart Anatomy: Charts, Values, Templates, and Releases The folder structure every chart shares, and the three concepts that make Helm more than a YAML bundler. -
Create Your First Helm Chart from Existing Manifests Turn a Deployment, Service, and ConfigMap into a templated chart, then install and upgrade it. -
One Chart, Many Environments: Values Files for Dev, Staging, and Prod Override just what differs between environments, and understand the order values are merged in. -
Manage Releases: Install, Upgrade, Rollback, and Uninstall The commands that cover a release's whole lifecycle, and how revision history makes rollback possible.