skip to content

Delivery & Extensibility

Helm run by a person, by a pipeline, or not run at all because a controller rendered the chart first, plus the hooks Helm offers for extending it. Each context changes what release history means.

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

explore

questions

20

Why run both helm lint and helm template as merge checks on a chart?

level: juniorimportance: must knowfreq 68%

answer

  1. One is a gate, one is evidence
  2. Exit code versus printed manifests
  3. Both render without a cluster
  4. Diff the render, not the chart
  5. Server schema checks need --dry-run=server

basics

~20 s

helm lint loads the chart, renders it and reports structural problems as an exit code, so it works as a pass/fail gate. helm template prints the manifests the chart produces, so a reviewer can diff the YAML the change actually generates.

solid answer

~40 s

They answer different questions. `helm lint` checks that the chart is well formed - required `Chart.yaml` fields, a SemVer `version`, parseable values, templates that render into parseable YAML - and exits non-zero on an error, which is what makes it a gate; `--strict` promotes warnings to errors. `helm template` renders the chart to stdout so the pipeline can diff the rendered output of the pull request against the base branch, which is where a one-line values change that halves a memory limit becomes visible. Both render locally: neither knows your cluster's API versions or CRDs, `lookup` returns nothing, and no admission rule is consulted. For real schema validation you need a cluster, via `--dry-run=server`, which a pull-request job usually has no credentials for.

code

bash · 7 lines
bash
helm lint charts/payments-ledger --strict -f envs/prod.yaml

helm template ledger charts/payments-ledger -f envs/prod.yaml > pr.yaml
git stash
helm template ledger charts/payments-ledger -f envs/prod.yaml > base.yaml
git stash pop
diff -u base.yaml pr.yaml

go deeper

for a junior

Be ready to say plainly that lint gives a pass/fail exit code while template prints the rendered YAML, and that neither one needs a cluster. Knowing that a failing lint turns the check red is the level's expectation.

for a middle

Explain the mechanics: what lint actually inspects in Chart.yaml and templates, what --strict changes, and why rendering with each environment's values file finds failures the defaults hide.

for a senior

Show that you know the blind spots and how you cover them: capability defaults, empty lookup results, no schema validation, and where a server-side dry run fits given that pull-request jobs rarely hold cluster credentials.

for a principal

Own the question of what the merge gate is for. Decide which checks are mandatory on every pull request, which need credentials and therefore belong later in the pipeline, and how rendered diffs get in front of reviewers without drowning them.

`helm lint` and `helm template` answer two different questions, which is why a chart pull request normally runs both. Lint answers *is this chart well formed?* and reports the verdict as an exit code. Template answers *what YAML does this chart actually produce?* and prints it. The first is a gate; the second is evidence that a human, or a diff tool, can read. ## What helm lint checks `helm lint CHART` takes either a chart directory or a packaged `.tgz`. It loads the chart, checks that `Chart.yaml` carries the required fields - `apiVersion`, `name`, `version` - and that `version` is valid SemVer 2, checks that `values.yaml` parses, then renders every file under `templates/` and checks that the result parses as YAML. Findings print under an `==> Linting` header as `[INFO]`, `[WARNING]` and `[ERROR]` lines; any error makes the command exit non-zero, and that exit code is the whole reason it works as a merge check. `--strict` promotes warnings to errors so a chart cannot quietly accumulate them. Lint accepts `-f` and `--set` exactly as install does. That matters more than it looks: the chart defaults often render fine while the values file an environment really uses trips a `required` call or a template that indexes a key that file does not set. Linting once with defaults and once with each real values file catches a whole class of failures before a cluster ever sees them. Lint can also be asked to descend into subcharts instead of examining only the top-level chart. ## What helm template does `helm template NAME CHART` runs the same rendering pipeline an install would run, but writes the manifests to stdout instead of sending them anywhere. `--show-only templates/deployment.yaml` narrows the output to a single file, and `-f`, `--set` and `-n` behave as they do on install. Because the output is deterministic text, the pattern worth knowing is the rendered diff: render the chart as it stands on the target branch, render it again with the pull request's changes and the same values, and diff the two. A values-only change that drops a container's memory limit is a one-line chart diff and a loud rendered diff, and the rendered diff is the review the change actually needs. ## What neither command knows Both render locally, and that shared limitation is what an interviewer is listening for. `.Capabilities.KubeVersion` and `.Capabilities.APIVersions` come from the CLI's built-in defaults rather than from your cluster unless you pass `--kube-version` or `--api-versions`. A `lookup` call returns nothing, because there is no cluster to look in. A CustomResourceDefinition that your chart's custom resources depend on may not exist anywhere. Nothing checks that an image reference is pullable, that the target namespace exists, or that an admission rule would reject the object. YAML that parses can still be rejected by the API server for a field that does not exist in that kind's schema. ## Getting real validation For that you need the cluster. `--dry-run=server` sends the rendered manifests to the API server, which validates them against the real schemas and rejects unknown fields, without persisting anything. In Helm 4 `--dry-run` takes a value - `client` or `server` - and `helm template`'s older `--validate` flag is deprecated in favour of `--dry-run=server`, with the two mutually exclusive. The catch is credentials: a pull-request job, especially one triggered from a fork, usually has none. The common split is lint plus template on every pull request, where no cluster is needed, and a server-side dry run in a job that already holds deploy credentials. ## A concrete merge check For a payments-ledger chart at version 2.14.3 the check runs `helm lint charts/payments-ledger --strict -f envs/prod.yaml`, then renders the chart with the same values and diffs that render against the one produced from the base branch. A change that lowered the default `resources.limits.memory` from `768Mi` to `256Mi` passes lint cleanly - it is valid YAML and a valid quantity - and appears as two changed lines in the rendered diff. That is the division of labour: lint stops malformed charts, the render shows intent. ## Two things candidates get wrong First, lint is not a policy check. It has opinions about chart conventions, not about whether your Deployment sets a security context, so a green lint says nothing about compliance. Second, neither command touches a release: they never create, upgrade or read the `sh.helm.release.v1.*` records, so a green merge check tells you the chart renders, not that the next upgrade will succeed against the live objects.

  • A chart renders fine under helm template but the upgrade is rejected by the API server. How is that possible?
    `helm template` only proves the output is parseable YAML. It does not check field names, types or required fields against the schema of each kind, because it never talks to an API server; the built-in capability defaults may also not match the cluster. A misspelled field or a kind whose API version the cluster no longer serves surfaces only when something applies it - which is what `--dry-run=server` exists for, since it validates against the real schemas without persisting anything.
  • Why lint with each environment's values file rather than only the chart defaults?
    Rendering is values-dependent. A template guarded by `required` or one that indexes a nested key only fails when a values file omits that key, and the chart defaults usually supply everything. Linting with the production values file catches the case the defaults hide. It costs one extra command per file and is the cheapest way to stop an environment-specific render failure from being discovered by the deploy job.
  • Should the merge check run helm lint on the chart directory or on the packaged tarball?
    The directory is what the pull request changes, so lint it there for fast feedback. Linting the packaged `.tgz` additionally proves that packaging worked and that `.helmignore` did not exclude a file the templates need, so a pipeline that packages on merge often lints both: the directory on the pull request, the tarball right after `helm package`.

saying these in an interview costs you the question

  • Claiming helm lint validates manifests against the cluster
  • Thinking helm template contacts the API server
  • Treating a green lint as a policy or security check
  • Expecting lookup to return live objects during a render
  • Believing helm template creates or reads a release
  • Linting only with defaults and never with real values files

context

open as a page

How does applying helm template output with kubectl differ from running helm install?

level: juniorimportance: must knowfreq 74%

basics

~20 s

helm template only renders a chart to YAML on stdout; helm install renders it, applies it, and records the release in the namespace. Objects applied from rendered YAML have no release record, so helm list, helm history and helm rollback see nothing.

open as a page

What does Helm's --post-renderer flag do to a chart's rendered manifests?

level: juniorimportance: must knowfreq 40%

basics

~20 s

Helm pipes its rendered manifest stream into another program's stdin and uses the YAML that program returns on stdout. That returned YAML becomes the release's manifest, so you can patch a chart you do not own without forking it.

open as a page

How do you run one Helm chart across dev, staging and production without forking it?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Keep one chart whose values.yaml holds the defaults true everywhere, then pass a small per-environment file at deploy time: helm upgrade -f values/prod.yaml. Only keys that genuinely differ live in that file; the chart is identical everywhere.

open as a page

Why do deploy jobs run helm upgrade --install rather than helm install?

level: middleimportance: must knowfreq 74%

basics

~20 s

helm install fails when the release name already exists and helm upgrade fails when it does not, so neither is safe to re-run. helm upgrade --install picks whichever applies, letting one deploy job serve a first deploy and every later one.

open as a page

Why does helm plugin install verify signatures by default in Helm 4, and how do you opt out?

level: middleimportance: must knowfreq 50%

basics

~20 s

A plugin is code Helm executes locally with your kubeconfig and credentials, so Helm 4 defaults helm plugin install --verify to true and refuses a plugin it cannot verify. The opt-out is the explicit --verify=false.

open as a page

What does a Helm plugin's plugin.yaml declare, and how do you install one?

level: juniorimportance: should knowfreq 45%

basics

~20 s

plugin.yaml is a Helm plugin's descriptor: apiVersion v1, a name and version, a required type naming the extension point (such as cli/v1), and a runtime, either subprocess or extism/v1. You add one with helm plugin install.

open as a page

What happens to a chart's helm.sh/hook manifests when a controller applies helm template output?

level: middleimportance: should knowfreq 58%

basics

~20 s

They are still rendered into the output unless --no-hooks is passed, so they get applied as ordinary objects in the same batch as everything else. No hook event fires, hook-weight orders nothing, and hook-delete-policy deletes nothing.

open as a page

In Helm 4, what does --post-renderer accept, and what broke from Helm 3?

level: middleimportance: should knowfreq 46%

basics

~20 s

Helm 4's --post-renderer takes the name of an installed postrenderer/v1 plugin, not a path to an executable as in Helm 3. Pipelines that passed a script path stop working until that script is installed as a plugin and referenced by name.

open as a page

What goes wrong when a deploy job runs helm upgrade --install without --version?

level: seniorimportance: should knowfreq 56%

basics

~20 s

A chart reference with no --version resolves to whatever the repository currently offers as newest, so the same pipeline run twice can deploy two different charts. The deploy job has no fixed input and the result is not reproducible.

open as a page

How would you choose between the subprocess and extism/v1 runtimes for an internal Helm plugin?

level: seniorimportance: should knowfreq 33%

basics

~20 s

Choose subprocess when the plugin needs host access - kubeconfig, cloud credentials, other CLIs - and accept a build per OS and architecture. Choose extism/v1 for one portable WebAssembly artefact and a sandbox bounding what it reaches.

open as a page

A Helm post-renderer patches a chart's StatefulSet; a colleague's helm upgrade without the flag drops the patch. Why, and how do you prevent it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Helm stores a release's values but not the fact that a post-renderer produced its manifest. An upgrade without the flag renders the chart plainly, so the patched fields are absent from the new desired state and Helm removes them. The fix is to make the flag impossible to omit.

open as a page

Why promote a packaged, version-pinned Helm chart instead of upgrading from the branch checkout?

level: seniorimportance: should knowfreq 55%

basics

~20 s

A branch checkout is not a fixed artifact: each environment renders whatever the branch held when it deployed. Packaging once with helm package and installing that exact chart version everywhere leaves the values file as the only deliberate difference.

open as a page

What should helm package freeze into a chart, and what should the deploy job decide?

level: principalimportance: should knowfreq 38%

basics

~20 s

helm package freezes templates, defaults, Chart.yaml version and appVersion, and vendored dependencies into one versioned tarball. The deploy job supplies what varies per target: release name, namespace and values. The real decision is which side owns the application version.

open as a page

Across a platform, when should Helm itself install a chart rather than a controller applying rendered YAML?

level: principalimportance: should knowfreq 36%

basics

~20 s

Keep Helm as the installer where you need the install half: hook ordering, chart tests, crds/ handling, waiting, rollback and a release record. Render ahead of apply where the applier already supplies ordering, pruning and health.

open as a page

In Helm, how do you decide what belongs in chart defaults versus a per-environment values file?

level: principalimportance: should knowfreq 42%

basics

~20 s

Chart defaults carry everything true of every environment. The per-environment file carries only the axes that legitimately differ — sizing, wiring and identifiers. A difference in behaviour, such as a feature enabled only in production, is debt to budget rather than configuration.

open as a page

What can a getter/v1 Helm plugin do that a cli/v1 plugin cannot?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

A getter/v1 plugin registers URL schemes and is called by Helm during any fetch that uses one, so it works inside helm repo add, pull, install and dependency update. A cli/v1 plugin only adds a subcommand someone types.

open as a page

How does a chart's values.schema.json keep three per-environment values files to one shape?

level: middleimportance: nice to knowfreq 29%

basics

~20 s

values.schema.json sits in the chart root, and Helm validates the merged values against it on install, upgrade, template and lint. An environment file that misspells a key or supplies the wrong type fails there, before anything renders.

open as a page

How do you hand objects applied from rendered Helm chart YAML over to helm upgrade --install?

level: seniorimportance: nice to knowfreq 33%

basics

~20 s

Helm refuses to take over live objects unless they carry the label app.kubernetes.io/managed-by: Helm plus meta.helm.sh/release-name and meta.helm.sh/release-namespace annotations matching the target release. Either stamp that metadata on each object, or run the install with --take-ownership.

open as a page

Which parts of a Helm chart's output never reach a post-renderer's stdin?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Manifests in the chart's crds/ directory are never templated and never enter the stream, so a post-renderer cannot patch them. Helm's own sh.helm.release.v1 record Secret is not rendered either. Everything under templates/, including subchart templates and hook manifests, does go through.

open as a page