Skip to content
DevOps AI ToolKit
All guides
AI for Automation By James Joyner IV · · 15 min read

Engineers: Stop 2 AM Rollouts with Helm Values Validation & CI

Practical steps for engineers to enforce Helm values validation with values.schema.json and chart‑testing CI. Catch bad configs in PRs and avoid 2 AM...

Engineers: Stop 2 AM Rollouts with Helm Values Validation & CI

Helm validates chart input by applying values.schema.json (JSON Schema) to the final merged .Values during helm install, upgrade, lint, and template. Combine that with chart-testing scenarios in CI to catch configuration errors before they reach a cluster. Add the schema at the chart root, wire it into your pipeline, and most bad values get caught at review time instead of 2 AM.


TL;DR:

  • Helm validates the merged .Values object against values.schema.json during install, upgrade, lint, and template commands, including subcharts with their own schemas.
  • The schema must reside at the chart root and include $schema, type definitions, properties, required keys, and additionalProperties to be effective.
  • YAML coercion can cause validation errors; quote version and port strings to prevent unintended type changes, and use JSON Schema’s if, then, else for conditional logic.
  • Validation fails early if remote $ref links are used; vendor schemas into the chart to avoid unreachable references, especially in air-gapped environments.
  • Integrate chart-testing in CI to catch schema, structural, and deployment errors across multiple scenario files before releasing to production.

Table of Contents

How Helm’s values validation actually works

Here’s the part people get wrong: Helm doesn’t validate your values.yaml file in isolation. It validates the final .Values object, the one assembled after merging chart defaults, parent overrides, --set flags, and any -f files you passed on the command line. If that merged result doesn’t satisfy values.schema.json, Helm stops before rendering templates.

According to the Helm project’s own documentation, this validation kicks in on four commands:

  • helm install, when you’re standing up a release for the first time
  • helm upgrade, when you’re changing an existing release’s values
  • helm lint, when you’re checking a chart before packaging or committing it
  • helm template, when you’re rendering manifests locally or in a CI dry run

Subchart schemas get enforced too. If a dependency chart ships its own values.schema.json, the portion of your merged values that maps to that subchart has to satisfy it, regardless of what the parent chart’s schema says. There’s also a --skip-schema-validation flag, added specifically for cases like air-gapped clusters where a schema references a remote resource Helm can’t reach. It’s a valid escape hatch, but it’s not something you want baked into a normal deploy path, since it removes the one automated check standing between a typo and a broken rollout.

Create values.schema.json: minimal structure and placement

Helm looks for values.schema.json in one place: the chart root, next to Chart.yaml and values.yaml. No configuration flag points Helm at it. If it’s not there, Helm skips schema validation entirely and falls back to whatever defaults your templates handle gracefully, or don’t.

A working minimal schema needs four things:

  1. $schema, declaring which JSON Schema draft you’re targeting, usually http://json-schema.org/schema# or a specific draft URL
  2. type: object, since .Values is always a map at the root
  3. properties, one entry per key you want validated, each with its own type and constraints
  4. required and additionalProperties, controlling which keys must exist and whether unlisted keys are tolerated

Here’s a schema for a chart with a replica count and a service port:

{
  "$schema": "http://json-schema.org/schema#",
  "type": "object",
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 1
    },
    "service": {
      "type": "object",
      "properties": {
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        }
      },
      "required": ["port"]
    }
  },
  "required": ["replicaCount", "service"]
}

Map every property directly to its path in values.yaml, nesting objects the same way you nest your YAML. Keep schemas maintainable by matching the chart best practices recommendation to document every value you expose: a comment above each values.yaml entry describing its purpose and type makes the schema easier to keep in sync when someone adds a field six months from now and forgets the schema exists.

JSON Schema features and Helm-specific nuances to use

JSON Schema is strict about types in a way YAML is not, and that gap causes more validation headaches than anything else. YAML happily interprets port: 8080 as an integer and enabled: yes as a boolean, but it can also coerce things you didn’t intend, like turning an unquoted version string 1.20 into a float and dropping the trailing zero. Quote any value that’s meant to stay a string, especially version numbers, ports written as strings, and anything with a leading zero.

additionalProperties decides how strict your schema is:

  • Set it to false on an object to reject any key that isn’t explicitly listed, useful for public or platform charts where you want tight control over input.
  • Leave it unset or true when a chart is still evolving and you’d rather not block contributors adding new optional fields.
  • Apply it selectively: strict at the top level, permissive on a nested block you know will grow.

For mutually exclusive configuration, like choosing between “use an existing secret” or “create one from provided fields,” JSON Schema’s conditional keywords let you express that relationship directly with if, then, and else, instead of relying on template logic to catch the conflict at render time.

One more nuance: avoid remote $ref pointers in a chart schema when you can. A schema that pulls in a definition from an external URL will fail validation the moment that URL is unreachable, which is exactly the air-gapped scenario that made --skip-schema-validation necessary in the first place. Vendor the referenced schema into your chart repository instead.

Vendored schema separated from remote dependency

Pro Tip: Run helm template --debug after every schema change. It renders faster than a full install and shows you the merged values Helm actually validated, not what you assumed it would.

Subcharts, parent overrides, and how validation propagates

Chart dependencies complicate validation in a way that trips up a lot of teams the first time they hit it. The rule, confirmed by Helm’s own test suite and issue tracker, is straightforward: the final .Values object is checked against every schema in the dependency tree, and a parent chart cannot override or bypass a subchart’s schema requirements. If a subchart requires postgresql.auth.password to be a non-empty string, setting it to an empty string from the parent chart’s values.yaml still fails, no matter what the parent’s own schema allows.

Practical patterns that keep this from becoming friction:

  • Mirror the subchart’s expected structure exactly in your parent values.yaml comments, so anyone editing the file can see which keys belong to which dependency.
  • Test parent overrides against the subchart’s schema locally with helm template before assuming an override will work in CI.
  • Pin subchart versions in Chart.yaml so an upstream schema change doesn’t silently break your parent chart’s deployments.

When a subchart’s schema genuinely conflicts with how your platform needs to configure it, vendoring the subchart into your own repository or wrapping it with a thin adapter chart is often less painful than fighting the upstream schema on every release. Platform teams managing several internal charts on top of community subcharts do this specifically to keep schema expectations aligned across the dependency graph, since Helm’s validation behavior makes it clear there’s no other way to relax a subchart’s own requirements.

Validate values in CI: chart-testing and installation tests

Schema validation catches structural problems: wrong types, missing keys, disallowed fields. It won’t catch a combination of otherwise-valid values that breaks your application at runtime. That’s what chart-testing (ct) is for, and it’s the piece most teams skip until a bad combination reaches production.

A solid ct-based pipeline looks like this:

  1. Detect changed charts. ct identifies which charts changed in a pull request by default, so you’re not re-testing your entire chart repository on every commit.
  2. Lint per values file. ct lint runs helm lint, yamllint, and chart-specific checks against every file matching a convention like ci/*-values.yaml, so a single chart can be tested against several realistic configurations, not just its defaults.
  3. Install-test each scenario. ct install spins up a real (or kind-based) cluster and installs the chart using each of those same values files, catching problems that only surface once Kubernetes actually tries to reconcile the resources.
  4. Layer in template dry runs. Running helm template and helm install --dry-run against each scenario file adds a fast pre-check before the full ct install cycle, useful for catching schema errors in seconds instead of waiting on a cluster spin-up.

A repeatable setup that works well ties per-PR ct lint to every changed chart, runs a matrix job for ct install against your essential scenario files (staging config, high-availability config, minimal config), and gates merges on both passing. According to chart-testing’s documentation, this per-values-file approach is built into ct specifically because validating only a chart’s default values.yaml misses the configurations teams actually run in production.

Common pitfalls, operational trade-offs, and fixes

Most schema validation failures fall into a handful of repeat offenders. Knowing the pattern makes them faster to diagnose.

  • Remote $ref failures in air-gapped environments. A schema referencing an external URL fails the moment that URL isn’t reachable. Vendor the reference into your chart repository, or isolate the exception behind a documented flag rather than disabling validation chart-wide.
  • Overuse of --skip-schema-validation. The flag exists for a reason, specifically air-gapped clusters where remote references can’t resolve, but it should be an audited, logged exception, not a routine fix for “the schema is annoying.” If your CI pipeline uses it by default, you’ve quietly turned off your safety net.
  • YAML type coercion surprises. Unquoted values that look like numbers or booleans get coerced by the YAML parser before Helm’s schema even sees them. Quote strings explicitly, and where a template needs to normalize a value, Sprig template functions handle the conversion more predictably than hoping the YAML parser guesses right.
  • Schemas too strict too early. A brand-new chart with a locked-down schema slows down the exact experimentation it needs in its first few weeks. Start permissive, tighten additionalProperties and required as the chart stabilizes, and enforce the stricter version in CI once the interface is settled.

Pro Tip: Keep a short changelog entry every time you tighten a schema. The next engineer who hits a validation failure will want to know if it’s a new rule or a bug in their values file.

Step-by-step example: add a schema, test locally, and validate in CI

Here’s the full loop, from a bare chart to a CI-gated one.

  1. Write a minimal values.yaml. Something like replicaCount: 2, server.port: 8080, and feature.enabled: false covers the common case: a count, a network setting, and a toggle.
  2. Write the matching values.schema.json. Declare replicaCount as an integer with a minimum of 1, server.port as an integer between 1 and 65535, and feature.enabled as a boolean, then mark all three required with additionalProperties: false at each object level.
  3. Verify locally. Run helm lint . first, since it’s the fastest feedback loop. Follow with helm template . to see the rendered manifests, then helm install my-release . --dry-run --debug to confirm the merged values Helm would actually use in a real install.
  4. Add CI scenario files. Create ci/default-values.yaml, ci/high-availability-values.yaml, and ci/minimal-values.yaml, each representing a real deployment shape your chart supports.
  5. Wire up ct in your pipeline. Run ct lint --config ct.yaml to check every scenario file against the schema, then ct install --config ct.yaml to confirm each one deploys cleanly.
  6. Read failures and fix both sides. A schema error naming a field that shouldn’t exist usually means either the scenario file has a typo or the schema is stricter than it should be. Fix whichever one is wrong, then re-run before committing.

This loop is quick once it’s set up. The setup cost is a values file, a schema file, and a ct.yaml config, and it pays for itself the first time someone’s --set typo gets caught in a pull request instead of a rollout.

Verify and debug validation errors: commands and typical error messages

When validation fails, helm lint and helm template give you the fastest read on what broke and where.

  • “expected integer but got string” usually means an unquoted or misformatted value in your values file, or a --set flag that passed a string where the schema wants a number.
  • “missing required property” points to a key the schema requires that’s absent from your merged values, often because a subchart’s schema requires something the parent chart never set.
  • “additionalProperties not allowed” flags a key that doesn’t exist in the schema at all, commonly a typo or a leftover field from a previous chart version.
  • Run helm install --dry-run --debug when the error’s source isn’t obvious. It prints the fully merged values, including everything contributed by subcharts, so you can see exactly what Helm validated.

Once you fix a validation error, update both the schema and your ci/*-values.yaml scenario files, so the same mistake gets caught automatically next time.

Practical checklist for hardening charts in production

Generating a schema by hand for a chart with dozens of values is tedious, and AI-assisted generation has become a practical shortcut for getting a first draft fast, an approach covered in more depth in generating values.schema.json with AI. The output still needs a human pass to confirm required fields and type constraints match what the chart actually expects, but it beats writing 40 properties from scratch.

Pair that with a pre-merge testing habit, outlined in testing Helm charts before they reach production, and a working checklist looks like this:

  • Run ct lint against every scenario file before merging, not just the default values.
  • Run helm template for each target environment (staging, production, disaster recovery) to catch environment-specific schema mismatches.
  • Block --skip-schema-validation from protected branch pipelines, and require a documented reason whenever it’s used elsewhere.

When schema validation is worth the maintenance cost

Schemas aren’t free. Writing and maintaining values.schema.json for a chart nobody else touches is often wasted effort. But for platform charts, anything shared across teams, or anything published for external users, a strict schema turns a vague deployment failure into a specific, readable error message before a single pod gets scheduled. That trade favors strict validation every time you can’t personally vouch for who’s setting the values.

For a chart still in active development, a permissive schema (or none yet) lets you iterate without fighting your own guardrails. The moment that chart stabilizes or gets a second user, tighten it and enforce it in CI with an audited skip process rather than an open one. Teams that need help building that pipeline can find audit and consulting options at DevOps AI Toolkit.

— James

How DevOps AI Toolkit helps you harden Helm charts

Devopsaitoolkit

Writing a correct schema is one afternoon of work. Catching every place your charts, values files, and CI pipeline disagree with each other is the part that actually eats a week, and it’s the part a fresh set of eyes tends to catch faster than the team that’s been staring at the chart for months. A Kubernetes Health Check for $300 gives you a focused review of your chart’s schema coverage, values structure, and where validation gaps are letting bad input through. If your validation problems are tangled up with broader infrastructure code, a Terraform / IaC Audit at $250 covers that ground too.

For teams that want to build the schema and CI habit themselves, the site also runs a Docker Academy Individual Pro membership at $19 per month, plus a library of AI prompt packs and troubleshooting guides for the rest of the Kubernetes and Helm workflow. Check current plans on the pricing page, or book a review directly through the work-with-me page if you’d rather have someone else find the gaps first.

Sources

FAQ

How do I get all values from a Helm chart?

Run helm show values <chart> to see a chart’s default values.yaml before installing it, or helm get values <release> to see the values currently applied to a deployed release. Add --all to helm get values to include values inherited from the chart’s own defaults, not just the ones you explicitly set.

How do I update values in a Helm chart?

Edit your values file or pass --set key=value on the command line, then run helm upgrade <release> <chart> -f values.yaml to apply the change. Helm validates the newly merged values against values.schema.json as part of that upgrade, so a bad edit gets caught before it reaches your cluster.

Is Helm deprecated?

No. Helm remains an actively maintained project under the CNCF, with ongoing feature work, including the --skip-schema-validation flag added in a 2024 pull request. It’s the standard packaging tool for Kubernetes applications and continues to receive regular releases.

What is the order of precedence for values in a Helm chart?

Helm applies values in layers, with each layer overriding the one before it: chart defaults from values.yaml, then parent chart values for subcharts, then -f files in the order they’re passed, then --set and --set-string flags last. The final merged result from all these layers is what gets validated against values.schema.json.

Where does Helm’s schema file need to live?

Helm looks for values.schema.json at the root of the chart directory, next to Chart.yaml and values.yaml. If it’s missing, Helm skips schema validation for that chart entirely, though subchart schemas still apply to their portion of the merged values.

Newsletter

Free: the DevOps AI Incident-Triage Cheat Sheet

Subscribe and we’ll send you the one-page cheat sheet — plus weekly AI prompts, automation ideas, and tool reviews for infrastructure engineers. One email a week. No spam, unsubscribe anytime.

  • AI Incident-Triage Cheat Sheet (PDF)
  • Access to 2,778 DevOps AI prompts
  • One practical workflow email per week
Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.