Why does `helm get manifest` omit a release's hook resources, and which command shows them?
answer
- The render is sorted into two piles
- One annotation decides which pile
- Only one pile is the stored release manifest
- A sibling subcommand prints the other pile
- A delete policy can remove the live object
basics
~20 sHelm splits a render into ordinary manifests and hook manifests - anything carrying a helm.sh/hook annotation. Only the ordinary ones become the release manifest that helm get manifest prints; the hooks are stored separately and printed by helm get hooks.
solid answer
~40 sWhen Helm renders a chart it partitions the output. Any manifest annotated `helm.sh/hook` is pulled aside and tracked as a hook, with its event, `helm.sh/hook-weight` and `helm.sh/hook-delete-policy`; everything else becomes the release's manifest - the set Helm applies and stores for that revision. `helm get manifest <release>` prints that stored set, so a migration Job that is a hook will never appear there, however hard you grep. `helm get hooks <release>` prints the hook manifests, annotations included. Both accept `--revision`. `helm get notes` prints the rendered NOTES.txt, `helm get metadata` gives a machine-readable summary of the release, and `helm get all` prints the lot in one go. A hook can also be absent from the *cluster* while still appearing in `helm get hooks`, because a delete policy removed the object after it ran.
code
bash · 11 lines# The applied, non-hook YAML stored for the current revision
helm get manifest tileserv -n geo
# The hook manifests, with their annotations
helm get hooks tileserv -n geo
# Everything Helm stored for an older revision, in one stream
helm get all tileserv -n geo --revision 26
# A structured summary for a script
helm get metadata tileserv -n geo -o jsongo deeper
Be ready to say that hook resources are stored separately from the rest of a release and have their own subcommand, so not finding a Job in the manifest output proves nothing.
Explain the partition: the helm.sh/hook annotation moves a document out of the applied manifest set, and Helm tracks it with its event, weight and delete policy instead. Name the get subcommands.
Show the inspection habit: reach for hooks before concluding a resource is missing, recognise that a delete policy explains an absent live object, and prefer the structured metadata output in automation.
Own the consequence for the platform: work that lives in hooks is invisible to anything auditing release manifests, so a fleet-wide inventory built on stored manifests alone will under-report what actually runs.
### The render is partitioned before anything is applied Rendering a chart produces a stream of YAML documents. Before Helm applies any of it, it sorts the documents into two piles by looking for one annotation: - a document carrying `helm.sh/hook` is a **hook**. Helm records it along with the events it is attached to, its `helm.sh/hook-weight` ordering and its `helm.sh/hook-delete-policy`, and runs it at the appropriate point in the operation. - everything else is part of the release's **manifest**: the set of objects Helm applies and then stores as the definition of that revision. `helm get manifest <release>` prints the second pile only. That is not an omission bug; it reflects how Helm models the two kinds of object. The manifest is the desired state Helm owns and reconciles across upgrades. Hooks are one-shot work attached to an event, and Helm does not track them as part of the release's resource set. ### The practical consequence Someone inspecting the release `tileserv` in namespace `geo` - a geospatial tile server installed from an 18-chart umbrella called `tile-platform` - goes looking for the schema-migration Job the team swears runs before every upgrade: ``` $ helm get manifest tileserv -n geo | grep -c 'kind: Job' 0 ``` Nothing. The natural conclusion, that the chart never rendered it, is wrong: ``` $ helm get hooks tileserv -n geo | grep -A2 'helm.sh/hook:' helm.sh/hook: pre-upgrade helm.sh/hook-weight: "-5" helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded ``` The Job is there, it is a `pre-upgrade` hook, and its delete policy explains a second puzzle: `kubectl get jobs -n geo` may show nothing either, because the object was deleted once it succeeded. The manifest Helm stores and the objects currently alive in the namespace are two different questions, and hooks are the place where they diverge most sharply. ### The rest of the `get` family `helm get` has one subcommand per stored artefact of a release: - `helm get manifest` - the applied, non-hook YAML for a revision. - `helm get hooks` - the hook manifests, annotations intact. - `helm get values` - the configuration, user-supplied or (with `--all`) computed. - `helm get notes` - the rendered `NOTES.txt`, which is otherwise only seen once, in the output of the install that produced it. - `helm get metadata` - a compact, machine-readable summary of the release: its name, namespace, revision, chart name and version, appVersion, status and deployment time. It is the one to reach for from a script, because it is structured by design rather than being human output you have to parse. - `helm get all` - every one of the above in a single stream, which is what you want when you are about to open a ticket or hand the state to a colleague. `--revision N` works across the family, so you can pull the manifest and hooks of an older revision as easily as the current one. ### Reading `helm get manifest` correctly Two further points regularly separate a confident answer from a shaky one. First, this output is what Helm **stored**, not a fresh render. It reflects the chart version and the values that were in effect when that revision was created. If the chart has moved on in your repository, the stored manifest will not match a render of the working copy, and that difference is meaningful information rather than an error. Second, it is a statement about what Helm applied, not about what is running now. Confirming that the live objects still match what the manifest says is a separate step with separate tools. ### Why Helm splits them at all The two piles have different lifecycles, which is the real reason for the partition. The release manifest is desired state that Helm owns: on the next upgrade Helm compares the new manifest against the stored one, and a resource that has disappeared from the chart is deleted from the cluster because Helm knows it used to be part of the set. Hooks are not in that set. They are one-shot work bound to an event, so Helm never reconciles them across revisions and never deletes a hook object because the chart stopped rendering it - the only thing that removes a hook object is its own delete policy, or a person. Keeping the two piles separate in the stored record is what makes that difference expressible at all, and it is why the inspection commands are separate too. ### Why this is a differentiator rather than a screening question Nobody is rejected for not knowing that hooks live outside the manifest. But an engineer who has spent twenty minutes grepping `helm get manifest` for a Job that was always a hook remembers the split permanently, and the answer shows that a candidate has actually inspected releases rather than only installing them.
- A Job appears in `helm get hooks` but `kubectl get jobs` in that namespace shows nothing. What happened?The hook's `helm.sh/hook-delete-policy` removed the object after it ran - `hook-succeeded` deletes it on success, and `before-hook-creation` clears the previous one before the next run. The manifest Helm stored is unaffected by that deletion, so the hook keeps showing up in `helm get hooks` for every revision that contained it while leaving no live object behind.
- Which `helm get` subcommand would you use from a script that needs the running chart version, and why not the others?`helm get metadata`, with `-o json`. It returns a structured summary - name, namespace, revision, chart name and version, appVersion, status and deployment time - designed to be parsed. The alternatives are either whole YAML streams you would have to search or human-formatted output whose layout is not a contract.
- Why can `helm get manifest` differ from re-rendering the same chart from your working copy?Because it prints what was stored at the time the revision was created: the chart version installed then, and the values in effect then. Your working copy may be several chart versions ahead, and the values you would pass locally may differ from what the last operator passed. The stored manifest is the authoritative record of what Helm applied; a local render is a statement about the chart you have now.
saying these in an interview costs you the question
- Assumes hooks appear in helm get manifest output
- Concludes a missing Job means the chart never rendered it
- Thinks helm get manifest re-renders the chart live
- Believes helm get hooks reads live cluster objects
- Expects helm get all to print only values
- Cannot name a subcommand for the rendered NOTES.txt