Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 11 min read

Helm Hooks for Ordered Releases and Database Migrations

Helm installs everything at once unless you tell it not to. Learn how pre-install, post-upgrade, and delete hooks sequence migrations and avoid broken releases.

  • #kubernetes
  • #helm
  • #migrations
  • #ci-cd
  • #release-management

The first time I shipped a Helm chart with a database migration baked in, the migration Job and the new app Deployment came up at the same instant. The app crashed against a schema that did not exist yet, the migration finished thirty seconds later, and I spent an awkward minute watching CrashLoopBackOff while the release “succeeded.” Helm renders and applies your whole chart in one shot. If you need ordering, you need hooks.

Helm hooks are the mechanism for “do this thing at a specific point in the release lifecycle.” They are how you run schema migrations before the app starts, seed data after an install, or clean up a temporary resource when a release is removed. I lean on an AI assistant to draft the annotations and weights because the syntax is fiddly and easy to get subtly wrong, but I always read what it produced before applying anything.

What a hook actually is

A hook is an ordinary Kubernetes manifest with a special annotation. Helm pulls it out of the normal release, applies it at the declared phase, and waits. Here is a migration Job as a pre-upgrade and pre-install hook:

apiVersion: batch/v1
kind: Job
metadata:
  name: "{{ .Release.Name }}-db-migrate"
  annotations:
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
spec:
  backoffLimit: 2
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: "myorg/app:{{ .Values.image.tag }}"
          command: ["./migrate", "up"]

The pre-install,pre-upgrade phase means Helm runs this Job and waits for it to complete before applying the Deployment. The app never sees the old schema.

Weights control order within a phase

Multiple hooks in the same phase run in ascending weight order. Lower numbers run first; ties break alphabetically by name. If you have a “create the database” hook and a “run migrations” hook, weight them so the order is deterministic:

metadata:
  annotations:
    "helm.sh/hook": "pre-install"
    "helm.sh/hook-weight": "-10"   # runs before weight -5

Weights are strings that Helm parses as integers, so always quote them. Negative numbers are fine and common for pre-phases. This is the single most useful detail to remember: the weight is the contract for ordering, not the position of the resource in your templates/ directory.

Delete policies stop hooks from piling up

A hook resource sticks around after it runs unless you tell Helm to remove it. Without a delete policy, your second upgrade fails because a Job named myrelease-db-migrate already exists. The hook-delete-policy annotation handles this:

  • before-hook-creation — delete the prior hook resource before creating the new one (this is what saves you on repeated upgrades)
  • hook-succeeded — delete after it finishes cleanly
  • hook-failed — delete after it fails

I almost always use before-hook-creation,hook-succeeded. The first keeps upgrades repeatable; the second keeps the namespace tidy. I deliberately drop hook-succeeded when a migration fails rarely but catastrophically, because keeping the failed Job around lets me read its logs.

Pro Tip: If a hook Job fails, Helm marks the whole release as failed and stops. The resources it already applied stay applied. Run helm status <release> and kubectl logs job/<release>-db-migrate before you reach for helm rollback — the logs almost always tell you exactly what broke.

The full lifecycle, not just pre-install

Helm fires hooks at these points: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete, pre-rollback, and post-rollback. Two underused ones:

A post-install hook is great for a smoke test that confirms the app answers before you call the release done:

apiVersion: v1
kind: Pod
metadata:
  name: "{{ .Release.Name }}-smoke"
  annotations:
    "helm.sh/hook": "post-install,post-upgrade"
    "helm.sh/hook-weight": "5"
    "helm.sh/hook-delete-policy": "hook-succeeded"
spec:
  restartPolicy: Never
  containers:
    - name: curl
      image: curlimages/curl:8.8.0
      command: ["curl", "-fsS", "http://{{ .Release.Name }}.{{ .Release.Namespace }}.svc/healthz"]

A pre-delete hook lets you drain a queue or take a final backup before the release tears down. Just remember that --wait and hook timeouts interact: helm upgrade --wait --timeout 10m applies to hooks too.

Test hooks are a separate thing

The helm.sh/hook: test annotation is special. Those resources do not run during install or upgrade — they run only when you call helm test <release>. Keep your validation Pods under that annotation so CI can run them on demand without slowing real deploys. I covered the broader testing workflow in testing Helm charts before they reach production, and hooks are how that machinery hangs together.

Where AI helps and where it must not

Hook annotations are exactly the kind of dense, repetitive YAML that an AI assistant drafts well. I describe the sequence in plain English — “create the schema, then migrate, then start the app, then smoke test” — and let a tool like Claude or Cursor generate the weighted annotations. It gets the before-hook-creation policy right far more often than I do from memory. I keep a few of these in my prompt library, and there is a Helm-focused prompt pack if you want a starting set.

What the AI never gets is cluster access. It produces YAML; I read it, I run helm template and helm upgrade --dry-run myself, and I am the one who types the command that touches the cluster. Treat the model as a fast junior engineer who is excellent at boilerplate and has zero context on your production data. Anything that mutates a real cluster — especially a migration that rewrites a schema — goes through a human and a dry run first. Never hand the model a kubeconfig or prod credentials.

If you want a second set of eyes on the generated chart before it ships, our code review dashboard flags missing delete policies and unquoted weights, which are the two mistakes that bite people most.

Conclusion

Helm hooks turn “apply everything and hope” into a defined sequence: migrate before the app starts, smoke test after it does, clean up when it leaves. Quote your weights, set before-hook-creation so upgrades stay repeatable, and keep failed Jobs around long enough to read their logs. Let AI write the annotations, but keep the dry run and the cluster credentials firmly in human hands. More patterns live under kubernetes-helm.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.