Git, CI/CD & GitOpsLII · Kubernetes CIManifestValidation
Manifest validation with kubeconform — schemas, strict mode, version pinning
What you'll learn
- Run kubeconform against rendered Kubernetes manifests and read the output
- Distinguish strict mode from summary mode and choose the right one for CI
- Pin the Kubernetes version the schema check targets so the check is reproducible
- Identify what kubeconform catches that helm lint and conftest do not
Prerequisites
Practice
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
kubeconform is the canonical schema validator for rendered Kubernetes manifests. It reads a YAML stream, parses each document, and checks it against the OpenAPI schema of a specific Kubernetes version. The check is exhaustive: every field is type-checked, every enum is validated, every required field is enforced. The tool catches the class of error that every other validator in the pipeline misses — the schema drift between what the manifest declares and what the API server will accept.
What kubeconform checks
The OpenAPI schema is the API server’s source of truth for what a resource looks like. Every field has a type, every type has a validation pattern, every resource has a list of required fields, and every list has bounds. A manifest that passes helm lint can still fail kubeconform, because helm lint checks the chart structure and template syntax, not the API server contract.
kustomize build overlays/prod | kubeconform -strict -summary
helm template ./chart | kubeconform -strict -summary
The output, when the manifest is valid, is a single line per resource stating the kind and the API version:
Deployment/my-app apps/v1
Service/my-app v1
ConfigMap/my-app v1
When the manifest is invalid, kubeconform prints the document index, the resource kind, and the schema error:
Deployment/my-app apps/v1: json: unknown field "replicas"
Deployment/my-app apps/v1: json: missing field "selector"
The categories of error kubeconform catches:
- Unknown fields. A field that does not exist in the schema for the target Kubernetes version. Often a typo (
replicainstead ofreplicas) or a field that was valid in an older version and has been removed. - Missing required fields. A resource that omits a field the schema requires. For a Deployment:
apiVersion,kind,metadata.name,spec.selector,spec.template. - Type errors. A field whose value cannot be coerced to the schema type — a string in a numeric field, an integer in an enum, a malformed label.
- Enum violations. A field whose value is not in the allowed set — a
restartPolicythat is not one ofAlways,OnFailure, orNever.
Strict versus summary
The two modes differ in what counts as a failure:
# summary mode — print per-resource status, exit non-zero only on errors
kubeconform -summary manifests.yaml
# strict mode — fail on missing fields AND on unknown fields
kubeconform -strict -summary manifests.yaml
Summary mode reports parse errors and required-field violations but tolerates unknown fields. Strict mode fails on both. The strict mode is the production default; summary mode is a debugging aid.
The reason strict mode is the default is operational. A manifest with an unknown field is a manifest that will apply successfully today and break on the next Kubernetes upgrade, when the field is interpreted as garbage. The schema is the API server’s contract; the contract is the boundary. Strict mode enforces the boundary; summary mode walks up to it.
Version pinning
The schema kubeconform checks against depends on the -kubernetes-version flag:
kubeconform -kubernetes-version 1.36.0 -strict -summary manifests.yaml
The version pin is what makes the check reproducible. Without the pin, kubeconform uses the schema version bundled with the tool, and the bundled schema moves forward as the tool is updated. A check that passes today against kubeconform 0.6.7 may fail tomorrow against kubeconform 0.6.8 because the bundled schema has been updated.
The pin is also what makes the check relevant to the target cluster. A repository that deploys to a Kubernetes 1.28 cluster should check against the 1.28 schema, not the latest schema the tool ships. A field that is valid in 1.36 and was added in 1.30 will fail against the 1.28 schema; the failure tells the team the manifest is not safe to deploy to 1.28.
flowchart LR
A["Manifest"] --> B["kubeconform\n(-kubernetes-version 1.36.0)"]
B --> C{"Schema check"}
C -->|pass| D["Valid for 1.36"]
C -->|fail| E["Schema error\n(file:line, reason)"]
Production discipline
- Always pass
-strict. The unknown-field tolerance in summary mode is a footgun for production repositories. The strict mode is the rule. - Always pass
-kubernetes-version. A check without a version pin is a check against the schema the tool ships, not against the schema the cluster runs. - Vendor the schemas for air-gapped runners. A network-restricted runner that silently falls back to the bundled schema produces a check that does not match the target cluster.
- Render before validating. kubeconform reads rendered manifests, not templates. The render stage produces the YAML; the validate stage checks it. Skipping the render skips the review surface.
Cross-course references
- Kubernetes for Production Sysadmins - Part X (Manifests) covers the manifest format and the API server’s interpretation of it.
- Kubernetes for Production Sysadmins - Part XXXIII (Admission) covers OPA Gatekeeper and Kyverno, which check some of the same fields at admission time but cannot replace the schema check — the schema check is the only check that fails on unknown fields before the apply.
- This course, Part LII-01 (Foundations) - the render stage that produces the YAML kubeconform checks.
Quiz
Knowledge check · 4 questions
Q1. A team runs `helm lint` against a chart and considers that sufficient validation. What error class does `helm lint` miss that kubeconform catches?
Q2. Running kubeconform without `-kubernetes-version` produces a check against the schema version bundled with the kubeconform binary, which is what the target cluster runs.
Q3. Name the two kubeconform flags that make the schema check reproducible in CI, and state what each does.
Q4. Diagnose a kubeconform misconfiguration where the check passes locally but fails in CI, and recommend the fix.
A team runs `kubeconform -summary manifests.yaml` in CI. The check passes in the team's development environment against kubeconform 0.6.7. CI runs kubeconform 0.6.9 and the check now fails because the bundled schema for an unrelated resource changed. The team's kubeconform version is unpinned, and the version flag is not passed.
Passing score: 75%. Answers are checked in this browser.