skip to content

Hook Ordering

helm.sh/hook-weight sorts the hooks that share one event, lowest integer first, and negative weights are allowed. Asked because getting two hooks to run in a guaranteed order is a real problem with a small number of moving parts.

part ofHelmoverview, primer and where to startread it →
on this pageshow

questions

4

Two Helm pre-install hook Jobs carry no helm.sh/hook-weight - in what order do they run?

level: middleimportance: must knowfreq 55%

answer

  1. Absent is not unordered
  2. They share one value
  3. Deterministic, but not from file layout
  4. Naming decides it, so naming can break it
  5. Two characters of annotation removes the guess

basics

~20 s

Both default to weight 0, so they tie. Helm still runs them one at a time in a deterministic order that falls back to the resource name, not the template file order. Give them explicit weights instead.

solid answer

~50 s

With no `helm.sh/hook-weight` annotation both Jobs are weight 0, so the weight comparison cannot separate them. Helm's sort is stable and deterministic - it falls back to the hook resource's **name**, not the order of the files under `templates/` and not the order the templates were rendered in - and it then executes them sequentially: create the first Job, wait for it to complete, create the second. So you do get a repeatable order, but it is a property of what you named the objects. Rename a Job, add a `fullnameOverride`-style prefix from values, or let an enabled subchart contribute its own weight-0 hook to the same event, and the order changes with no diff in the part of the chart you were thinking about. If two hooks genuinely have to run in a fixed sequence, say so explicitly: `"-5"` and `"0"`, or `"0"` and `"10"`.

code

yaml · 29 lines
yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: fanout-seed-topics
  annotations:
    "helm.sh/hook": pre-install
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: seed
          image: registry.example.internal/fanout-tools:1.4.2
          args: ["seed-topics"]
---
apiVersion: batch/v1
kind: Job
metadata:
  name: fanout-verify-topics
  annotations:
    "helm.sh/hook": pre-install
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: verify
          image: registry.example.internal/fanout-tools:1.4.2
          args: ["verify-topics"]

go deeper

for a junior

Know that a missing weight annotation means 0, not 'unordered', and that two such hooks therefore tie. The takeaway to state out loud: if the order matters, write the weights down rather than hoping.

for a middle

Explain the mechanics - the tie-break is deterministic and comes from the rendered resource name, hooks are created one at a time with a wait for each to finish, and a failure stops the rest of the phase.

for a senior

Show why the deterministic order is still a trap in production: names are values-driven, renames look cosmetic in review, and a subchart can drop a new weight-0 hook into the same pool. Then give the fix and the numbering convention.

for a principal

Own the standard across charts: a documented weight band per concern, a lint or review rule that rejects unweighted hooks, and a position on whether sequencing belongs in the chart at all rather than in the application's own startup path.

### What a tie actually is Every hook manifest fires with an integer weight. An absent `helm.sh/hook-weight` annotation is not a special "unordered" marker - it is the value 0. Two unannotated `pre-install` hook Jobs are therefore two hooks at the same weight, competing inside the same event's pool. ### Helm still gives you a single, repeatable order Helm does not create tied hooks concurrently, and it does not shuffle them. The sort is stable and the tie-break is deterministic: when weights are equal, Helm compares the hook resources' names. Whatever your two Jobs are called, one of them sorts first, and it will sort first again on the next install with the same chart. Execution is then strictly sequential regardless of weights: Helm creates the first hook resource, waits until it reaches a finished state - a Job must reach completion, a bare Pod must reach `Succeeded` - and only then creates the second. If the first Job fails, the operation aborts and the second Job is never created at all. So "they tie" never means "they race"; it only means you did not choose which one goes first. ### Why the repeatable order is still the wrong thing to rely on The order is derived from the rendered resource names, and resource names in a real chart are rarely constants. They are usually built from a helper template that folds in the release name or a values-driven override, which means the winning hook can change when someone installs the release under a different name, or when a values file sets a name prefix, or when a Job is renamed for readability in a pull request that looks entirely cosmetic. Nothing in the chart's diff says "this reorders your hooks". The second way the tie moves is subcharts. Hooks contributed by an enabled subchart are flattened into the same release and sorted into the same pool for that event. A dependency bump that adds one weight-0 `pre-install` Job to a subchart you do not maintain drops a third competitor into your tie, and where it lands depends on its name too. The chart you own did not change. ### The fix is to stop tying Annotate. Weights are cheap, they read as documentation, and they survive renames: ```yaml annotations: "helm.sh/hook": pre-install "helm.sh/hook-weight": "-10" # provision first ``` ```yaml annotations: "helm.sh/hook": pre-install "helm.sh/hook-weight": "0" # then the job that consumes it ``` Leave gaps between the numbers - `-20`, `-10`, `0`, `10` - so the next hook can be inserted without renumbering everything and without re-creating the tie you just removed. Remember the value must be a quoted string; an unquoted number is a YAML integer and the API server refuses an annotation value that is not a string. And remember an unparsable value falls back to 0, which is exactly the tie you were trying to escape - a typo in the weight puts the hook straight back into the default group with no error. ### Confirming the order you actually get Because the annotation is just text in the rendered output, the cheapest check is to render the chart and read the hook manifests' weights directly rather than reasoning about them. Watching a real install is the other half: the hook resources appear one at a time in the namespace, in the order Helm chose, and the gap between them is the completion of the previous one. If you see two hook Jobs created at the same instant, something other than Helm's hook machinery is creating them - hooks are never issued in parallel. ### The interview point The weak answer is "they run in the order they appear in the templates directory", which is a guess about file layout that Helm never consults. The other weak answer is "it is random" - also wrong, and it leads people to build retry loops around a problem that a two-character annotation solves. The strong answer names the default weight, says the tie-break is deterministic but derived from naming, notes that hooks are executed serially with a wait between them, and finishes with the recommendation: if the order matters, encode it.

  • Do two hooks at the same weight run in parallel?
    No. Helm processes hooks one at a time whatever their weights: it creates a hook resource, waits for it to reach a finished state, then creates the next. Equal weights only mean Helm chose the order for you; they never mean the two hooks are created together, and a failure in the first stops the second from being created at all.
  • A dependency bump added a subchart hook on the same event. How can that change your ordering?
    Subchart hooks are flattened into the same release and sorted into the same pool for that event, so a new weight-0 hook from a dependency competes with your unweighted hooks and lands wherever the tie-break puts it. You cannot edit the subchart's annotation, so the defence is to weight your own hooks well away from 0.
  • Does relying on the template file names under templates/ ever work for hook ordering?
    No - Helm does not order hooks by file name or by directory position at any point. The only inputs are the parsed weight and, for a tie, the rendered resource name. Charts that appear to work 'because of the file order' are usually working because the resource names happen to sort the same way.

saying these in an interview costs you the question

  • Says hooks run in templates/ file order
  • Says the order is random or unpredictable
  • Thinks tied hooks are created in parallel
  • Assumes an unweighted hook runs last
  • Relies on resource names for ordering in production
  • Adds sleeps or retries instead of weights

context

open as a page

In a Helm chart, what does the helm.sh/hook-weight annotation control?

level: juniorimportance: should knowfreq 46%

basics

~20 s

helm.sh/hook-weight is a quoted integer that sorts the hooks firing on the same event, lowest first. Negatives are allowed and a missing annotation means 0. Helm runs each hook to completion before creating the next.

open as a page

A Helm pre-upgrade hook Job fails because a ConfigMap the same chart renders does not exist yet - why?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Pre-upgrade hooks are a phase that finishes before Helm applies any of the chart's ordinary manifests, and hook-weight orders hooks only against other hooks. Make the ConfigMap a hook too, or move the work to post-upgrade.

open as a page

Why would you give a Helm chart hook a negative helm.sh/hook-weight?

level: middleimportance: nice to knowfreq 24%

basics

~10 s

Because 0 is the default, every hook that omits the annotation sits at 0 - including hooks from subcharts you do not control. Only a negative weight is guaranteed to run ahead of them.

open as a page