Git, CI/CD & GitOpsLII · Kubernetes CITemplateValidation
Helm and Kustomize validation — lint, template, and build
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
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.yamlfields, 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.yamlthat 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 buildsucceeds. 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 buildoutput is schema-valid. The render is structural YAML, but a field that does not exist in the target schema will passkustomize buildand 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:
| Command | Class | What it checks | What it misses |
|---|---|---|---|
helm lint | source-time | Chart structure, template syntax, naming | Render-time errors, schema |
helm template --validate | render-time | Render correctness, API server contract (local schema) | Pinned Kubernetes version, unknown fields if schema is stale |
kustomize build | render-time | Overlay structure, patch application, reference resolution | Schema, policy, security |
kubeconform -strict | render-time | Pinned schema, unknown fields, type errors | Policy, 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
- Run
helm lint --stricton every chart change. The strict flag promotes warnings to failures; the default is a debugging aid. - Run
helm template --validateand pipe to kubeconform. The render-then-validate pipeline is what produces a checked artifact. - Pin the kubeconform version. Without
-kubernetes-version, the schema check is against the tool’s bundled schema, not the target cluster. - 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
Q1. A team runs `helm lint` and `helm template` (without `--validate`) in CI and considers the chart validated. What error class escapes both?
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.
Q3. Give the canonical CI command that renders a Helm chart with values and validates the render against a pinned Kubernetes version.
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.