Skip to content

Helm Hooks and Lifecycle

Franklin García edited this page Sep 6, 2026 · 2 revisions

Helm Hooks and Lifecycle

Helm hooks are ordinary Kubernetes resources with helm.sh/hook annotations. Timonel does not need a special hook resource type: attach the annotations to a typed cdk8s-plus construct whenever the construct supports the Kubernetes resource you need.

Hook annotations

Common annotations are:

helm.sh/hook
helm.sh/hook-weight
helm.sh/hook-delete-policy

Common hook events include:

pre-install
post-install
pre-upgrade
post-upgrade
pre-delete
post-delete
test

Consult the Helm documentation for the complete lifecycle semantics supported by your Helm version.

Pre-install migration Job

import * as kplus from 'cdk8s-plus-33';
import { Rutter } from 'timonel';

const chart = new Rutter({
  meta: { name: 'orders', version: '1.0.0' },
});

new kplus.Job(chart.getChart(), 'DatabaseMigration', {
  metadata: {
    name: 'orders-migrate',
    annotations: {
      'helm.sh/hook': 'pre-install,pre-upgrade',
      'helm.sh/hook-weight': '-10',
      'helm.sh/hook-delete-policy': 'before-hook-creation,hook-succeeded',
    },
  },
  backoffLimit: 2,
  containers: [
    {
      name: 'migration',
      image: 'ghcr.io/example/orders-migrate:1.0.0',
    },
  ],
});

The hook is still a normal Kubernetes Job in the synthesized chart.

Post-install smoke test

new kplus.Job(chart.getChart(), 'SmokeTest', {
  metadata: {
    name: 'orders-smoke-test',
    annotations: {
      'helm.sh/hook': 'post-install,test',
      'helm.sh/hook-weight': '10',
      'helm.sh/hook-delete-policy': 'hook-succeeded',
    },
  },
  backoffLimit: 1,
  containers: [
    {
      name: 'smoke-test',
      image: 'curlimages/curl:8.16.0',
      command: ['sh', '-c', 'curl --fail http://orders/health'],
    },
  ],
});

Pin image versions in real environments according to your supply-chain policy.

Hook ordering

Helm hook weights are strings containing signed integers. Lower weights run first within the same hook lifecycle stage.

Example ordering:

-20  create prerequisites
-10  run schema migration
  0  default hook work
 10  smoke test

Do not rely on incidental file ordering for lifecycle behavior.

Delete policies

Common values include:

before-hook-creation
hook-succeeded
hook-failed

Choose a policy based on whether you need historical Jobs for debugging. Deleting both successful and failed hooks can make incident analysis harder.

RBAC for hooks

If a hook needs Kubernetes API access, create a dedicated ServiceAccount and only the permissions it needs. Do not automatically reuse a broad application or cluster-admin identity.

Timonel's AWS IRSA helper can be used when a hook also needs AWS permissions:

const serviceAccount = chart.addAWSIRSAServiceAccount({
  name: 'migration',
  roleArn: 'arn:aws:iam::123456789012:role/orders-migration',
});

Attach the ServiceAccount to the workload using the supported cdk8s-plus workload API for the version you have installed.

Templated hooks

If a hook field must depend on Helm values, use ValuesRef where the serialized structure supports it. Do not convert the entire Job to raw YAML just to template one field.

For a custom hook resource without a typed construct, object-form addManifest(object, id) remains the narrow fallback.

Testing hooks

First validate syntax and normal rendering:

helm lint ./dist/orders
helm template orders ./dist/orders

Then use an isolated cluster or namespace to validate real hook ordering and failure behavior:

helm upgrade --install orders ./dist/orders --namespace orders --create-namespace
helm test orders --namespace orders
kubectl get jobs -n orders

Operational recommendations

  • make hooks idempotent when they may run again after a failed release;
  • set bounded retry/backoff behavior;
  • use immutable images;
  • keep hook permissions narrow;
  • avoid embedding credentials in hook commands or values files;
  • keep migrations backward compatible with the application rollout when possible;
  • test install, upgrade, rollback, and uninstall paths separately.

Clone this wiki locally