skip to content

Authoring for Reuse

Designing a chart other teams install: what values.yaml exposes, what helpers get shared, how versions signal breakage, and what a review can check before a cluster ever sees it.

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

explore

questions

24

What does helm lint check in a chart, and what does it not catch?

level: juniorimportance: must knowfreq 72%

answer

  1. A check that never touches a cluster
  2. Three severity levels in the output
  3. Exit code follows the highest severity
  4. One flag promotes warnings to failures
  5. It renders only the branches your values reach

basics

~20 s

helm lint parses the chart metadata, renders the templates with the values it is given, and reports INFO, WARNING and ERROR findings. It never contacts a cluster, so a manifest that renders as valid YAML but is an invalid Kubernetes object still passes.

solid answer

~40 s

`helm lint` runs two kinds of rule over a chart directory or packaged `.tgz`. First the metadata rules: `Chart.yaml` exists and parses, `apiVersion` and `name` are present, the directory name matches `name`, `version` is valid SemVer, `values.yaml` parses, a `templates/` directory exists, an icon is recommended. Then it renders every template with the coalesced values and checks the output parses as YAML, flagging Kubernetes API versions it knows to have been removed. If the chart has a `values.schema.json`, the values are validated against it too. By default only an ERROR fails the run; `--strict` makes warnings fail as well. What it does not do is talk to an API server or check the rendered objects against the Kubernetes schema, and it only renders the branches the values you passed actually reach.

code

bash · 3 lines
bash
helm lint ./charts/fanout-operator --strict
helm lint ./charts/fanout-operator --strict -f ci/metrics-enabled-values.yaml
helm lint ./charts/fanout-operator --strict --with-subcharts

go deeper

for a junior

Be ready to say what the command is for in one breath: it renders the chart and reports metadata and template problems, without a cluster. Know that errors fail it and warnings do not unless you ask for that.

for a middle

Explain the mechanics: the metadata rules, the render step, the YAML parse of the output, the schema check, and the three severities mapped onto the exit code. Say plainly which classes of defect are outside its reach.

for a senior

Show how you wire it into a pull request: strict mode, one run per supported values file, subcharts included, and a clear account of what still needs a render-assertion step or a cluster behind it.

for a principal

Own the boundary. Argue what a syntax-level check is worth in a chart that many teams install, where you spend the next unit of effort, and how you keep a green lint from being mistaken for a release-readiness signal.

## What lint is, and what it is not `helm lint` is a static check over chart *source*. You point it at a chart directory or a packaged `.tgz` (`helm lint ./charts/fanout-operator`) and it applies a fixed set of rules, printing findings at three severities and a one-line summary of how many charts were linted and how many failed. It is the cheapest gate a pull request can run: no cluster, no credentials, no container, milliseconds. It is emphatically not `helm test`. That command installs nothing and asserts nothing on its own; it runs test-annotated Pods against a release that is already installed in a cluster. Lint is the opposite end of the pipeline — it runs before anything exists. ## The rules it applies Roughly two families. **Chart-shaped rules.** `Chart.yaml` must exist and parse; `apiVersion` and `name` must be present; the directory the chart lives in must have the same name as the `name` field; `version` must be valid SemVer; `values.yaml`, if present, must parse as YAML; a missing `templates/` directory is reported; a missing `icon` is an informational nudge, not a failure. **Render rules.** Lint actually renders the chart. Every file under `templates/` is executed as a Go template against the coalesced values, and the output is parsed as YAML. A missing function, a bad pipeline, an `index` into a nil value, a `required` helper that fires, unbalanced indentation that produces unparseable YAML — all of these surface here as errors, and they are the findings lint is genuinely good at. Lint also compares the `apiVersion`/`kind` pairs in the rendered output against a list of Kubernetes API versions it knows to have been removed, so a chart still emitting a long-dead API version gets flagged. **Values-schema rules.** If the chart ships a `values.schema.json`, lint validates the coalesced values against it before rendering, so a wrong type or a missing required key fails at lint time rather than at install time. ## Severities and exit codes Findings print as `[INFO]`, `[WARNING]` or `[ERROR]`. By default the command exits non-zero only when at least one ERROR was reported — warnings are advisory. `--strict` promotes warnings so that they fail the run too, which is what you normally want in CI once a chart is clean. `--quiet` suppresses the informational chatter and prints only warnings and errors. ## The values you pass are the coverage you get This is the part candidates miss. Lint renders with the values it has: the chart's own `values.yaml` plus anything you supply with `-f`, `--set`, `--set-string` or `--set-file`. A template body wrapped in `{{- if .Values.metrics.enabled }}` where the default is `false` is never executed, so a syntax error inside it is invisible to a default lint run and ships to whoever turns the flag on. The practical consequence is that lint is run once per supported values combination, not once per chart. Similarly, `--with-subcharts` is needed for the metadata rules to be applied to each dependency under `charts/` as its own chart. Files under `crds/` are never templated at all, so nothing in your values changes them and nothing lint does exercises them. ## What it cannot catch Everything downstream of *is this valid YAML*. Lint has no Kubernetes object schema, so `replicas: three` in a Deployment, a misspelled `contaienrs` key, a Service `targetPort` that no container exposes, a name longer than the 63 characters Kubernetes allows for a Service, a selector that matches nothing — none of them are lint findings. It cannot see the cluster's real Kubernetes version or its installed CRDs, cannot know whether an image tag exists, and has no opinion about whether the resulting workload would ever become ready. That gap defines the rest of a chart's pre-merge suite: lint proves the chart renders, a values schema proves the inputs are shaped correctly, assertions over `helm template` output prove the rendered objects say what you meant, and only a server-side dry run or a real install proves the API server will accept them. ## Using it in CI Run it per values file, with `--strict`, as the first step — it is fast and its failures are unambiguous. Treat a clean lint as a statement about syntax and metadata, never as a statement about correctness.

  • What does --with-subcharts change about a helm lint run?
    It lints each dependency under `charts/` as a chart in its own right, so the metadata and values rules are applied to them too. Without it you get findings only for the parent chart, even though rendering the parent already renders the subcharts' templates as part of the output.
  • Does helm lint tell you anything about the files in crds/?
    Very little. Files in `crds/` are plain YAML that Helm never templates, so the render rules never exercise them and your values cannot change them. They are also left out of `helm template` output unless you pass `--include-crds`, which is why a render-assertion job on an operator chart often reports zero CRDs until someone notices the flag.
  • Your lint is green and a colleague's run on the same commit fails. What differs?
    Almost always the values. Lint renders what it is given, so a different `-f` file, a stray `--set`, or `--strict` on one side and not the other changes which templates are executed and which severities fail the run. Pin the values files and the flags in the repository so every run is the same run.

Lint is the spell-checker on a contract: it catches a misspelling and a missing signature block, and has nothing to say about whether the clauses mean what you intended.

saying these in an interview costs you the question

  • Thinks helm lint validates manifests against the Kubernetes API
  • Believes lint fails on warnings by default
  • Assumes lint covers templates the default values never render
  • Confuses helm lint with helm test against a live release
  • Thinks lint inspects dependencies without being asked
  • Calls a green lint proof that the chart installs

context

open as a page

What makes a Helm chart's values.yaml defaults safe to install unedited?

level: juniorimportance: must knowfreq 64%

basics

~20 s

Defaults that render runnable YAML with no -f file: every key a template reads is present and typed, empty extension points declared as {} or [], nothing secret or site-specific baked in, and required inputs failing the render loudly.

open as a page

Why does one failing subchart in an umbrella Helm release affect every other service in it?

level: middleimportance: must knowfreq 62%

basics

~20 s

An umbrella install is one release, so an upgrade is one operation with one outcome: work already applied stays live, the release is marked failed, and an automatic rollback reverts every service, not just the broken one.

open as a page

Why must a Helm chart's Deployment selector carry only a subset of the labels the chart emits?

level: middleimportance: must knowfreq 62%

basics

~20 s

Because a Deployment's spec.selector cannot be changed after creation. Charts emit version-bearing labels such as helm.sh/chart and app.kubernetes.io/version that change on every version bump, so the selector must hold only stable labels - normally name and instance.

open as a page

How does a checksum/config pod annotation in a Helm chart restart Pods when a ConfigMap changes?

level: middleimportance: must knowfreq 66%

basics

~20 s

The chart hashes its rendered ConfigMap template and writes the hash into the Pod template's annotations. When the config changes the hash changes, the Pod template changes, and the workload controller performs a normal rolling update. Helm itself never reads the annotation.

open as a page

In a Helm Chart.yaml, what does type: library change about how Helm treats the chart?

level: middleimportance: must knowfreq 45%

basics

~20 s

A library chart is not installable and emits no manifests of its own. Helm loads it only as a dependency of another chart, so its named templates become available to the application chart that pulls it in.

open as a page

Which changes to a published chart's values.yaml are breaking for consumers, and how do you ship one?

level: seniorimportance: must knowfreq 58%

basics

~20 s

A values change is breaking when an unchanged consumer values file now fails to render, silently renders something different, or forces a destructive upgrade. Ship one in a major chart version, with a template that fails loudly on the removed key during a migration window.

open as a page

What is an umbrella Helm chart, and how does it differ from one release per service?

level: juniorimportance: should knowfreq 55%

basics

~20 s

An umbrella chart is a parent chart whose content is mostly its dependencies on other charts. Installing it produces ONE Helm release with one revision history, so everything in it upgrades and rolls back together. Per-service releases give each its own history.

open as a page

In a Helm chart, which built-in objects supply the app.kubernetes.io/instance, managed-by and version label values?

level: juniorimportance: should knowfreq 55%

basics

~10 s

app.kubernetes.io/instance comes from .Release.Name, managed-by from .Release.Service (which always renders Helm), and version from .Chart.AppVersion. The separate helm.sh/chart label is built from .Chart.Name and .Chart.Version.

open as a page

Which Helm commands enforce values.schema.json, and what exactly is validated?

level: middleimportance: should knowfreq 46%

basics

~20 s

Helm applies a chart's values.schema.json during install, upgrade, lint and template. It validates the final coalesced values — chart defaults merged with every -f file and --set — before any template is rendered, not the user's own file in isolation.

open as a page

In a Helm chart's values.yaml, when do you nest keys and when do you keep them flat?

level: middleimportance: should knowfreq 55%

basics

~20 s

Nest to group the knobs of one thing, so a caller overrides a single leaf without restating its siblings - maps merge key by key. Keep it shallow: as a subchart, every path gains your chart's name as a prefix.

open as a page

In a helm create chart, why does the image tag default to .Chart.AppVersion?

level: middleimportance: should knowfreq 52%

basics

~20 s

So the chart installs unedited and ships a matching image: values.yaml sets image.tag to an empty string, which is falsy, so the default function falls back to the chart's appVersion. It couples the published appVersion to what an unedited install runs.

open as a page

An umbrella Helm chart bundles 14 services plus a third-party ingress-controller chart, and its 7-minute upgrade blocks every team. Which signals say split it?

level: seniorimportance: should knowfreq 44%

basics

~10 s

Split when deploy cadences diverge, when one team's failure blocks others, when the shared history no longer answers "what changed for my service", and when a bundled third-party chart upgrades on someone else's schedule.

open as a page

Your chart passes helm lint and helm template in CI, but helm install fails on an invalid object name. Why?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Neither command validates rendered objects against a Kubernetes API server, and helm template uses a short placeholder release name unless you pass one. A name helper that truncates to 63 characters and then has a suffix appended only overflows under a real, long release name.

open as a page

Every helm upgrade of a chart rolls all its Pods although the image and values are unchanged - how do you find the cause?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Diff the Pod template between the stored revisions, not the whole manifest. The usual culprits are version-bearing labels such as helm.sh/chart on the Pod template, a checksum annotation hashing a whole rendered file that includes those labels, or a non-deterministic function in the render.

open as a page

You fix a bug in a shared Helm library chart. What has to happen before the releases that depend on it run the fix?

level: seniorimportance: should knowfreq 33%

basics

~20 s

Every consumer must re-resolve its dependencies, repackage and upgrade its own release. A library chart is linked in before packaging, not fetched at install time, so publishing a new version changes nothing in a cluster on its own.

open as a page

In a Helm chart, how do you design escape hatches like extraEnv and podAnnotations?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Default them empty and typed, render them verbatim with toYaml piped into nindent inside a with guard so an empty value emits nothing, and pick the shape knowingly: maps merge across layered values files, lists are replaced wholesale.

open as a page

How would you set Helm chart granularity policy across a platform of many services and teams?

level: principalimportance: should knowfreq 36%

basics

~20 s

Default to one Helm release per independently deployable unit: the release is the unit of rollback, failure and concurrency. Allow umbrellas only where a set is genuinely installed as one product, and build the composition layer the split needs.

open as a page

Twelve teams each maintain their own Helm charts. Would you standardise them on one shared library chart?

level: principalimportance: should knowfreq 26%

basics

~20 s

Share only the invariants - labels, naming and truncation, image references - and leave workload shape to each chart. A shared library is a product with consumers: it needs an owner, semver, render-diff contract tests and automated bumps.

open as a page

How do you set the versioning policy for a Helm chart that many teams install?

level: principalimportance: should knowfreq 41%

basics

~20 s

Decide what the chart version promises. For a chart many teams consume, version it on its values contract and rendered output, let appVersion float independently, batch breaking changes into rare majors, use prereleases as an opt-in channel, and retire the chart with the deprecated flag.

open as a page

Two charts in one Helm release both define a template named common.labels - what happens?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Named templates share one global namespace across a Helm chart and everything under charts/. Nothing errors: one definition wins by load order and every caller in the release gets it. That is why library helpers are prefixed with the chart name.

open as a page

What does Helm's --devel flag change when you install or pull a chart?

level: middleimportance: nice to knowfreq 29%

basics

~20 s

It lets prerelease chart versions such as 2.97.0-rc.1 be selected. Helm normally skips any version with a prerelease segment when resolving, and --devel is documented as equivalent to asking for the range >0.0.0-0. An explicit --version wins over it.

open as a page

How would you design the pre-merge checks for a Helm chart many teams install?

level: principalimportance: nice to knowfreq 34%

basics

~20 s

Tier the checks by cost: lint and schema validation on every supported values file, then rendering plus targeted assertions on a small set of representative combinations, then a server-side dry run. Test the combinations you promise to support, not the exponential cross-product.

open as a page

How do you decide how wide to make a shared Helm chart's values.yaml surface?

level: principalimportance: nice to knowfreq 31%

basics

~20 s

Expose what installers legitimately vary, keep non-configurable what the chart exists to guarantee, and absorb the long tail with generic pass-through keys rather than a named key per request - because every key shipped is one strangers now depend on.

open as a page