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 ./mychart

The 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.json

Helm 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:

values.yaml
replicaCount: 2

image:
  repository: nginx
  tag: "1.27"

service:
  port: 80

resources:
  limits:
    cpu: 500m
    memory: 256Mi
deployment.yaml
apiVersion: 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 }}
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: quickstart
spec:
  ports:
    - port: {{ .Values.service.port }}

Running helm-schema ./quickstart produces:

values.schema.json
{
  "$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.

  • replicaCount flows into the Deployment’s spec.replicas, so it is an int32. Its description is Kubernetes’ own text for that field. The second anyOf arm (the pattern string) accepts Helm’s quoted-integer form — replicaCount: "2" renders and validates the same as 2, so the schema allows both.

  • service.port lands in a Service port and is typed the same way, with the Service field’s description.

  • resources is poured into the container’s resources field with {{ toYaml .Values.resources | nindent 12 }}. Because the whole subtree lands in a typed Kubernetes field, the schema expands to the full ResourceRequirements shape — limits, requests, and claims — each with its upstream description, even though values.yaml only sets limits.cpu and limits.memory.

  • image is an object with repository and tag. It is used in a string position ({{ .Values.image.repository }}:{{ .Values.image.tag }}), so the allOf guard records that image.repository must not be an array — the precise constraint the template implies, without over-claiming a full string type.

  • additionalProperties: false at the root means the schema lists exactly the values the chart uses. Keys the chart never reads are rejected, which is what catches typos like replicaCont.

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 $defs section to keep the file small — pass --no-minimize to 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-required

5. Where to go next#