Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLII · Kubernetes CITemplateValidation

Helm and Kustomize validation — lint, template, and build

Intermediate⏱ ~26 mingit

What you'll learn

  • Run helm lint against a chart and read the output
  • Use helm template to render and validate a chart against the API server schema
  • Run kustomize build against an overlay and read the output
  • Distinguish source-time validation (lint) from render-time validation (template, build)

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Helm and Kustomize are the two templaters the Kubernetes ecosystem has converged on. Both turn sources into the YAML the API server eventually receives. Both produce that YAML through a render step that CI can invoke. The validation story differs because the source model differs: Helm’s source is a chart with values, and the render is template expansion; Kustomize’s source is a base plus patches, and the render is structural transformation. The CI commands reflect the difference, and the three tools — helm lint, helm template, kustomize build — compose with kubeconform to produce the validated render.

helm lint

helm lint reads the chart, the values, and the templates against a list of common mistakes. The check is source-time: the templates are parsed but not expanded with values, and the chart structure is checked against the chart best practices.

helm lint ./chart

The output is a list of [INFO] and [WARNING] lines, plus a summary:

[INFO] Chart.yaml: icon is recommended
[WARNING] templates/deployment.yaml: object name does not conform to Kubernetes naming conventions
1 chart(s) linted, 0 failed(s)

What helm lint catches:

  • Chart structure. Missing Chart.yaml fields, malformed version strings, deprecated API versions.
  • Template syntax. Go template errors that prevent expansion.
  • Naming conventions. ServiceAccount names that exceed the 63-character limit, label keys that violate the optional prefix requirement.
  • Common values issues. Missing required values, values that override immutable fields.

What helm lint does not catch:

  • Render-time errors. A values.yaml that produces a syntactically valid template but a semantically broken manifest at expansion time.
  • Schema errors. Unknown fields, missing required fields, type errors. These are caught by kubeconform, not by helm lint.

helm lint --strict upgrades warnings to failures; --with-subcharts extends the check to chart dependencies. The strict mode is the production default; the default mode is a debugging aid.

helm template

helm template renders the chart with values and emits the YAML on stdout. The render is what the API server will see if helm install is run. Adding --validate turns the render into a schema check by parsing each emitted document against the local Kubernetes API version:

helm template ./chart
helm template ./chart --validate

The --validate flag is the bridge between source-time and render-time validation. A chart that passes helm lint can still produce a render that fails --validate because the render references a field the schema does not accept. The failure is the API server’s contract enforced locally.

flowchart LR
    A["helm lint"] -->|chart structure| B["Source-time"]
    C["helm template --validate"] -->|render + schema| D["Render-time"]
    B --> E["CI pipeline"]
    D --> E
    E --> F["kubeconform"]
    F --> G["Pinned schema check"]

The composition is the point. helm lint is fast (sub-second on most charts) and catches the cheap class of error. helm template is slower (seconds) and catches the render-time class. kubeconform catches the schema class against a pinned Kubernetes version. The three checks together cover what neither covers alone.

kustomize build

kustomize build reads an overlay and emits the rendered YAML. The overlay is a directory containing a kustomization.yaml that lists the base and the patches; the build walks the resources, applies the patches, and produces a stream of YAML on stdout:

kustomize build overlays/prod

Unlike Helm, Kustomize does not have a separate lint command. The validation story is simpler:

  • kustomize build succeeds. The overlay is structurally valid; the patches apply; the references resolve. A failure here is a structural error (a missing base, a malformed patch, a circular reference).
  • kustomize build output is schema-valid. The render is structural YAML, but a field that does not exist in the target schema will pass kustomize build and fail kubeconform. The schema check is downstream.

The two checks compose:

kustomize build overlays/prod | kubeconform -strict -summary -kubernetes-version 1.36.0

The pipe is the contract: the render must succeed, and the rendered output must pass the pinned schema check. A failure in either fails the pipeline.

Source-time versus render-time

The three commands split into two classes:

CommandClassWhat it checksWhat it misses
helm lintsource-timeChart structure, template syntax, namingRender-time errors, schema
helm template --validaterender-timeRender correctness, API server contract (local schema)Pinned Kubernetes version, unknown fields if schema is stale
kustomize buildrender-timeOverlay structure, patch application, reference resolutionSchema, policy, security
kubeconform -strictrender-timePinned schema, unknown fields, type errorsPolicy, security, image references

The split is the operational discipline. Source-time checks are fast and cheap; render-time checks are slower and authoritative. Both are required; neither replaces the other.

Production discipline

  1. Run helm lint --strict on every chart change. The strict flag promotes warnings to failures; the default is a debugging aid.
  2. Run helm template --validate and pipe to kubeconform. The render-then-validate pipeline is what produces a checked artifact.
  3. Pin the kubeconform version. Without -kubernetes-version, the schema check is against the tool’s bundled schema, not the target cluster.
  4. Render Kustomize overlays and pipe to kubeconform. The overlay build is structural; the schema check is downstream.

Cross-course references

  • Kubernetes for Production Sysadmins - Part X (Manifests) covers the Kustomize and Helm mechanics the validation assumes.
  • Kubernetes for Production Sysadmins - Part XXXIII (Admission) covers OPA Gatekeeper and Kyverno, which check policy at admission time that the validation stage does not cover.
  • This course, Part LII-02 (ManifestValidation) - the schema check kubeconform provides; this lesson renders into it.

Quiz

Knowledge check · 4 questions

  1. Q1. A team runs `helm lint` and `helm template` (without `--validate`) in CI and considers the chart validated. What error class escapes both?

  2. Q2. Running `kustomize build overlays/prod` and piping to kubeconform is sufficient validation for a Kustomize-only repository; helm lint is irrelevant when no Helm chart is involved.

  3. Q3. Give the canonical CI command that renders a Helm chart with values and validates the render against a pinned Kubernetes version.

  4. Q4. Diagnose a CI pipeline where `helm template` succeeds but `kubectl apply` fails because of a missing required field, and recommend the fix.

    A team runs `helm template ./chart` in CI and considers the chart validated when the render succeeds. A contributor edits the chart to add a new CronJob resource but forgets the required `spec.schedule` field. `helm template` renders the CronJob without error because Go templates do not enforce required fields. The apply fails at the API server with `missing required field "schedule"`. The team did not run `helm template --validate` or pipe to kubeconform.

Passing score: 75%. Answers are checked in this browser.