Quick start#
This walks through a first run and how to read the output. It assumes helm-schema is installed.
1. Point it at a chart#
The only required argument is the chart — a directory or a packaged .tgz/.tar.gz:
helm-schema ./mychartThe generated schema is written to standard output by default, so you can redirect it or inspect it directly. To write it where Helm will find it, use --output:
helm-schema ./mychart --output mychart/values.schema.jsonHelm automatically validates values against a values.schema.json next to values.yaml on install, upgrade, lint, and template.
2. A worked example#
Take this small chart. It sets a handful of values and uses them across a Deployment and a Service:
replicaCount: 2
image:
repository: nginx
tag: "1.27"
service:
port: 80
resources:
limits:
cpu: 500m
memory: 256MiapiVersion: apps/v1
kind: Deployment
metadata:
name: quickstart
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: app
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
resources: {{ toYaml .Values.resources | nindent 12 }}apiVersion: v1
kind: Service
metadata:
name: quickstart
spec:
ports:
- port: {{ .Values.service.port }}Running helm-schema ./quickstart produces:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"allOf": [
{
"additionalProperties": {},
"properties": {
"image": {
"additionalProperties": {},
"properties": {
"repository": {
"not": {
"type": "array"
}
}
}
}
}
},
{
"if": {
"anyOf": [
{
"not": {
"properties": {
"image": {}
},
"required": [
"image"
],
"type": "object"
}
},
{
"properties": {
"image": {
"enum": [
null
]
}
},
"required": [
"image"
],
"type": "object"
}
]
},
"then": false
},
{
"if": {
"anyOf": [
{
"not": {
"properties": {
"service": {}
},
"required": [
"service"
],
"type": "object"
}
},
{
"properties": {
"service": {
"enum": [
null
]
}
},
"required": [
"service"
],
"type": "object"
}
]
},
"then": false
}
],
"properties": {
"image": {
"additionalProperties": {},
"properties": {
"repository": {},
"tag": {}
},
"type": "object"
},
"replicaCount": {
"anyOf": [
{
"description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.",
"format": "int32",
"type": "integer"
},
{
"pattern": "^(([+-]_*)?(0|[1-9][0-9_]{0,17}|0[xX][0-9a-fA-F]{1,15}|0[bB][01]{1,62}|0[oO][0-7]{1,20}|0[0-7]{1,20})|[+-]?0[0-7]{0,8}[89][0-9]{0,8})$",
"type": "string"
}
]
},
"resources": {
"additionalProperties": {},
"description": "ResourceRequirements describes the compute resource requirements.",
"properties": {
"claims": {
"description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis field depends on the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
"items": {
"description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
"properties": {
"name": {
"description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
"type": "string"
},
"request": {
"description": "Request is the name chosen for a request in the referenced claim. If empty, everything from the claim is made available, otherwise only the result of this request.",
"type": "string"
}
},
"required": [
"name"
],
"type": "object"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"name"
],
"x-kubernetes-list-type": "map"
},
"limits": {
"additionalProperties": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
"properties": {
"cpu": {
"type": "string"
},
"memory": {
"type": "string"
}
},
"type": "object"
},
"requests": {
"additionalProperties": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
"type": "object"
}
},
"type": "object"
},
"service": {
"additionalProperties": {},
"properties": {
"port": {
"anyOf": [
{
"description": "The port that will be exposed by this service.",
"format": "int32",
"type": "integer"
},
{
"pattern": "^(([+-]_*)?(0|[1-9][0-9_]{0,17}|0[xX][0-9a-fA-F]{1,15}|0[bB][01]{1,62}|0[oO][0-7]{1,20}|0[0-7]{1,20})|[+-]?0[0-7]{0,8}[89][0-9]{0,8})$",
"type": "string"
}
]
}
},
"type": "object"
}
},
"type": "object",
"x-helm-schema-generated": true
}3. How to read it#
Nothing in the chart states a type for any of these values — yet the schema types all of them, because it followed each one into the resource field it renders into.
replicaCountflows into the Deployment’sspec.replicas, so it is anint32. Itsdescriptionis Kubernetes’ own text for that field. The secondanyOfarm (thepatternstring) accepts Helm’s quoted-integer form —replicaCount: "2"renders and validates the same as2, so the schema allows both.service.portlands in a Service port and is typed the same way, with the Service field’s description.resourcesis poured into the container’sresourcesfield with{{ toYaml .Values.resources | nindent 12 }}. Because the whole subtree lands in a typed Kubernetes field, the schema expands to the fullResourceRequirementsshape —limits,requests, andclaims— each with its upstream description, even thoughvalues.yamlonly setslimits.cpuandlimits.memory.imageis an object withrepositoryandtag. It is used in a string position ({{ .Values.image.repository }}:{{ .Values.image.tag }}), so theallOfguard records thatimage.repositorymust not be an array — the precise constraint the template implies, without over-claiming a full string type.additionalProperties: falseat the root means the schema lists exactly the values the chart uses. Keys the chart never reads are rejected, which is what catches typos likereplicaCont.
The schemas shown throughout these docs are fully inlined for readability. By default the tool interns subtrees that repeat across a large schema into a shared
$defssection to keep the file small — pass--no-minimizeto inline them, as shown here. See Output.
4. Common variations#
# Compact (single-line) JSON instead of pretty-printed
helm-schema ./mychart --compact --output mychart/values.schema.json
# Template analysis only — skip all Kubernetes/CRD schema lookups (fully offline)
helm-schema ./mychart --no-k8s-schemas
# Pin the Kubernetes version whose schemas are consulted
helm-schema ./mychart --k8s-version v1.34.0
# Mark unconditionally-guarded values as required on their parent object
helm-schema ./mychart --infer-required5. Where to go next#
- How it works — the analysis pipeline, phase by phase.
- Template analysis — how guards and control flow shape the schema.
- Kubernetes schemas — versions, offline use, and mirrors.
- CLI reference — every flag.