Helm / Intermediate: authoring and lifecycle contracts / Lesson 8
Values schemas and contract validation
Use JSON Schema and focused template checks to reject unsupported input shapes while distinguishing structural validation from application and policy guarantees.
On this page
Lesson goals
- Define required properties, types, ranges and nested objects.
- Explain schema defaults and unknown-property decisions.
- Choose schema constraints that express the public values interface.
Validate the public interface
A values schema defines acceptable input before generated objects reach Kubernetes. It can reject a string where replicas must be an integer, a missing required field or an unsupported service mode. The schema lives in values.schema.json and applies to the values contract rather than the entire rendered manifest. This catches mistakes closer to their cause and makes error messages easier to explain. Still, a schema does not prove that an image exists, a port is actually open or an organizational admission policy permits the generated object. Keep those environmental claims outside its scope.
Express types and ranges
The first schema fragment uses JSON Schema draft seven terminology for a small synthetic input. The required list names fields that must exist; properties describes their constraints. A minimum of zero deliberately permits a suspended application, while maximum ten is an illustrative product contract rather than a universal capacity recommendation. If zero is not meaningful for your application, choose another contract and explain it. Required alone does not make a string nonempty; add minLength where an empty string is invalid. Nested objects need their own required lists because top-level requirements do not recurse automatically.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["replicaCount", "service"],
"properties": {
"replicaCount": {"type": "integer", "minimum": 0, "maximum": 10},
"service": {
"type": "object",
"required": ["port"],
"properties": {"port": {"type": "integer", "minimum": 1, "maximum": 65535}}
}
}
}
The schema is complete JSON but remains non-runnable configuration. It was syntax parsed locally, not evaluated by a Helm schema engine.
Decide unknown-key policy
A misspelled replicaCout may be silently ignored unless the schema rejects unknown properties. additionalProperties false can catch that typo, but apply it thoughtfully: a chart may legitimately accept extension maps or global values from a parent. Locking the root too aggressively can reject valid dependency integration. Define strict objects for owned interfaces and deliberate open maps for application metadata where appropriate. Document whether custom annotations accept arbitrary string keys. An extensibility point should have a type contract of its own; openness does not require accepting arbitrary lists, objects and numbers everywhere.
Separate defaults from validation
Schema default is an annotation in JSON Schema; do not assume it injects a value into Helmโs Values object. Put operational defaults in values.yaml and use the schema to validate them. A template required call also treats empty values as missing, so it may reject false or zero even when the schema allows them. The second fragment is suitable for a genuinely nonempty repository string, not for a boolean enabled flag. Cross-field constraints can use schema alternatives or a carefully explained fail condition, but avoid conflicting messages spread across multiple layers.
{{- $repository := required "image.repository must be a nonempty string" .Values.image.repository -}}
image: {{ printf "%s:%s" $repository .Values.image.tag | quote }}
Assume image is an existing map and tag is already validated. This fragment does not validate registry availability or digest syntax.
Build positive and negative cases
Test defaults, a valid override and one invalid value for each meaningful constraint. Keep failures specific: an integer-type case should not fail first because service is missing. Include nested missing fields, wrong scalar types, zero where allowed and an unsupported enum value. Review schema evolution as an interface change. Tightening a range may reject existing installations even when their previous manifests worked. Provide a migration path and name the old accepted input. Syntax-only JSON validation is useful but cannot demonstrate actual schema behavior, so label the local check accurately when no schema engine is available.
Common errors and fixes
Schema default is expected to populate Values
- Why it happens
- A schema annotation was interpreted as value injection.
- How to fix it
- Declare defaults in values.yaml and validate their final merged form.
- Prevent it next time
- Review defaulting and validation as different mechanisms.
An intentionally false flag fails required
- Why it happens
- The template helper treats false as empty.
- How to fix it
- Use presence and boolean type checks instead of nonempty-string logic.
- Prevent it next time
- Include false and zero in contract cases.
A strict root rejects a valid global map
- Why it happens
- Unknown-property rejection ignored the parent integration contract.
- How to fix it
- Define allowed global and dependency interfaces intentionally.
- Prevent it next time
- Test standalone and parent-consumed chart contexts.
Check your understanding
1. How can a schema require a nonempty string image tag?
Show worked solution
Require the tag property in its containing image object, define its type as string and set minLength to one. Also require image at the appropriate parent if it must exist. That establishes presence and nonemptiness, not actual image existence or valid registry policy. Additional format constraints should match the chartโs documented tag or digest interface.
2. A new maximum rejects replicaCount 12 previously accepted. Is this only documentation cleanup?
Show worked solution
No. It changes the accepted values interface and can block future operations for an existing installation. Explain the reason, identify affected values and provide a compatible transition or a deliberate breaking-version policy. Do not justify the number as universal tuning: resource capacity, availability and application behavior need their own evidence.
Key takeaways
- Schemas constrain the values interface; they do not establish runtime or authorization guarantees.
- Defaulting, presence, type and nonemptiness are distinct decisions.