Why run both helm lint and helm template as merge checks on a chart?
answer
- One is a gate, one is evidence
- Exit code versus printed manifests
- Both render without a cluster
- Diff the render, not the chart
- Server schema checks need --dry-run=server
basics
~20 shelm 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 sThey 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 lineshelm 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.yamlgo deeper
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.
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.
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.
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