Topic 7.4
Helm Hooks & Chart Dependencies
In one line
Hooks let a chart run something at a specific point in its own lifecycle (like a database migration before the app starts); dependencies let one chart pull in and manage other charts it genuinely relies on.
Key ideas
- 01
A HELM HOOK is a Kubernetes resource (almost always a Job, Phase 1.4) annotated to run at a SPECIFIC POINT in a release's lifecycle —
pre-install/pre-upgrade(before the main resources are created/updated),post-install/post-upgrade(after), and several others — genuinely useful for exactly the kind of 'run this once, at this specific moment' task a plain chart install/upgrade doesn't otherwise have a clean way to express. - 02
The classic real use case: a
pre-upgradehook running a DATABASE MIGRATION Job before the application's own Deployment is updated to a new version that expects the new schema to already exist — ensuring the migration genuinely completes successfully BEFORE any new application code that depends on it actually starts running. - 03
A hook is just a normal Kubernetes Job with a special annotation (
"helm.sh/hook": pre-upgrade) — Helm specifically watches for this annotation and orchestrates running it at the correct point, waiting for it to complete successfully before proceeding with the rest of the release's normal resources. - 04
CHART DEPENDENCIES let one chart declare that it relies on OTHER charts — a genuinely common real example: your own application's chart declaring a dependency on the official, community-maintained PostgreSQL or Redis chart, rather than writing and maintaining your own Deployment/StatefulSet/Service manifests for a database you didn't build yourself.
- 05
Dependencies are declared in a
Chart.yaml'sdependenciessection (name, version, and a repository URL) —helm dependency updatedownloads the declared dependency charts into acharts/subfolder, and installing your OWN chart then automatically installs its dependencies too, as part of the same single release. - 06
This mirrors Docker Compose's own
depends_on(Docker's own course) at a conceptually similar level — 'this thing needs that thing to also exist' — though Helm dependencies specifically handle bundling and versioning OTHER PEOPLE'S charts cleanly, rather than just sequencing your own already-known services.
Code & diagrams
A pre-upgrade hook completes fully before the main upgrade proceeds — never running concurrently with it.
A real pre-upgrade migration hook — just a Job with one special annotation.
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-db-migration
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "0"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["./run-migrations.sh"]Declaring a real dependency on the official, community-maintained PostgreSQL chart.
# Chart.yaml
apiVersion: v2
name: my-app-chart
version: 1.0.0
dependencies:
- name: postgresql
version: "13.2.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled # can be toggled off via values.yamlWatch a hook run in order, and pull in a real dependency chart.
# Download the declared dependency chart(s) into charts/
helm dependency update ./my-app-chart
# Install — the migration hook runs FIRST, automatically, before the main app
helm install my-release ./my-app-chart
kubectl get jobs
# my-release-db-migration — Completed, before the Deployment's pods even started
# Confirm the dependency (e.g. PostgreSQL) was installed as part of the SAME release
kubectl get statefulsets
# my-release-postgresql — installed automatically alongside your own appExplain it without notes
Why is a pre-upgrade hook genuinely necessary for a database migration, rather than just adding the migration step inside the application container's own startup script?
Why would a team declare a dependency on the official community PostgreSQL chart rather than writing their own StatefulSet/Service manifests for it directly?
Practice
Add a pre-install hook Job to a test chart (even a trivial one that just echoes a message) and confirm with kubectl get jobs that it runs and completes before the chart's main Deployment appears.
Add a dependency on a public community chart (like the official Bitnami PostgreSQL or Redis chart) to a test chart's Chart.yaml, run helm dependency update, and confirm the dependency's resources are installed alongside your own.
Trade-offs
- ↔
Hooks and dependencies add real power and reduce duplicated effort, but they also mean a chart's true behavior isn't always fully visible just by reading its main templates — a migration silently running via a hook, or an entire database being installed as a side effect of a dependency declaration, can genuinely surprise someone unfamiliar with the chart's full structure; documenting a chart's hooks and dependencies clearly (in its own README) matters as much as writing them correctly in the first place.
Done when you can
I can write a Helm hook that runs a Job at a specific point in a release's lifecycle.
I understand why a pre-upgrade hook is a safer place for a database migration than an application's own startup script.
I can declare a chart dependency on another chart and confirm it installs alongside my own.