Featured image of post Ensuring the quality of Helm Charts

Ensuring the quality of Helm Charts

How to ensure the quality of the Helm charts we provide to development teams with an automated validation workflow in GitLab CI

Introduction

As an SRE / Platform Engineer, one of my responsibilities is to provide reliable Helm charts to the development teams. These charts are the foundation for deploying their applications on our Kubernetes platforms. A faulty chart means impacted applications, hours of investigation, and eroded trust in the components we provide.

The trigger for this work was an avoidable incident: undocumented breaking changes were introduced and broke every deployment using the chart, which forced us to ask ourselves a fundamental question: how do we guarantee the quality of the Helm charts we provide?

Context

The problem

Managing Helm charts by hand and without automated validation carries several risks:

  • Undocumented breaking changes: incompatible changes can be introduced without the consuming teams being informed.
  • Syntax errors: typos or YAML errors can go unnoticed until the production deployment.
  • Invalid templates: templates that do not render correctly or generate invalid Kubernetes manifests.
  • Functional regressions: changes that break existing features without being detected.
  • No traceability: hard to understand the history of changes and their impact.

The goal

I therefore needed a framework able to automatically validate each change before it is merged into the main branch, explicitly flag breaking changes, check the Kubernetes compliance of the generated manifests, test the expected behaviours and track changes in a usable changelog.

But it was not only about adding tools. The answer also had to be organisational: better frame contributions, make collaboration easier to follow, and make sure the quality of the chart no longer relies only on individual attention.

Components

The workflow is built around several tools and practices:

  • GitLab CI/CD: orchestrates the whole validation and publication pipeline.
  • Helm & plugins: the package manager for Kubernetes, with its native validation tools and its plugins.
  • Git Flow: branching strategy and Merge Request process.
  • SemVer: versioning to clearly communicate the type of changes.
  • Keep a Changelog: a standard format to document every change.

Pipeline architecture

The GitLab CI pipeline is organised in three main stages, each with a specific goal.

GitLab CI pipeline in three stages: lint with helm lint, validate with helm template, helm kubeconform and helm unittest, then release with helm package and the upload to the registry

Pipeline overview

Linting

The first level of validation in the pipeline checks the syntax and overall structure of the chart with Helm’s native helm lint command. It gives fast, immediately understandable feedback. Although it does not cover every case, it removes gross errors very early, before they reach the next steps, which avoids running deeper validations and wasting time.

1
helm lint .
πŸ“ Note

What Helm lint checks:

  • Correct YAML syntax
  • Chart structure (Chart.yaml, templates/, etc.)
  • Formatting issues
  • Warnings on best practices

Once the structure of the chart is confirmed, we can move on to more specific validation steps.

Template validation

To answer the question “does the chart actually render correctly with the provided values?”, I chose helm template, which renders the templates. This is often where you find missing variables, badly written conditions, or errors in Go Template functions.

It works simply: Helm interprets the templates and produces the final Kubernetes manifests, without applying them to a cluster. You get a concrete view of what would be deployed, which makes it a particularly useful step to detect rendering errors as early as possible.

1
helm template .
πŸ“ Note

What Helm template checks:

  • Correct rendering of Go templates (variables, functions, pipelines)
  • Resolution of values and of the helpers defined in _helpers.tpl
  • Correct syntax of {{- if }} conditions and {{- range }} loops
  • No undefined or wrongly referenced variables

Schema compliance

helm template guarantees that rendering works, but not that the result is acceptable to Kubernetes. That is precisely why I chose to use Kubeconform. This tool was selected because it is fast, easy to integrate into a CI, and very effective at validating manifests against known schemas.

It compares the generated resources to the expected Kubernetes schemas. So it does not only check that the YAML is well formed: it also validates the structure of the manifest, the allowed fields, the expected types and some version incompatibilities. With the CRD catalog added, it remains useful even in Kubernetes environments extended by operators.

Originally, this tool is a binary, but a Helm plugin was made so it can be used directly from the Helm CLI.

1
2
helm plugin install https://github.com/jtyr/kubeconform-helm
helm kubeconform .

Kubeconform configuration (.kubeconform):

1
2
3
4
5
6
schema-location:
  - default # Default K8s resources schema
  - https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json
summary: true
verbose: true
ignore-missing-schemas: true # Don't fail on resources where schema is not found.
πŸ“ Note

What kubeconform checks:

  • Invalid or deprecated fields
  • Incorrect data types
  • Missing values for required fields
  • Compatibility issues with the targeted Kubernetes version

Example output:

1
2
3
4
5
6
7
stdin - Secret mariadb-cluster is valid
stdin - StatefulSet mariadb-cluster is valid
stdin - Cluster pg-cluster is valid
stdin - MongoDBCommunity mongodb-cluster is valid
...
...
Summary: 17 resources found parsing stdin - Valid: 17, Invalid: 0, Errors: 0, Skipped: 0

Unit tests

I added Helm Unittest because, beyond syntax and compliance, the functional behaviour of the chart had to be protected. It is the tool that lets me state explicit expectations: a name must follow a convention, a resource must exist in a given case, an image must be built with a given value, etc.

The principle is close to a classic test framework, but applied to Helm rendering. You describe a set of input values, then express assertions on the produced manifest. This approach is particularly useful to avoid silent regressions when a chart evolves and starts handling several configuration variants.

1
2
helm plugin install https://github.com/helm-unittest/helm-unittest.git
helm unittest .

Unit tests check the expected behaviour of the chart in different configurations.

Example test (tests/deployment_test.yaml):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
suite: test deployment
templates:
  - deployment.yaml
tests:
  - it: should create a valid deployment
    set:
      image.tag: latest
    asserts:
      - isKind:
          of: Deployment
      - matchRegex:
          path: metadata.name
          pattern: -my-chart$
      - equal:
          path: spec.template.spec.containers[0].image
          value: nginx:latest
πŸ“ Note

These tests check that:

  • The default values produce valid manifests
  • The different configurations work correctly
  • Critical behaviours are preserved (non-regression)

Release management

Publication is only triggered when a Git tag is created on the main branch. This process guarantees that only tagged versions are published, and makes it possible to accumulate several changes before creating a release.

Key point: the CHANGELOG

Every Merge Request must include an update of CHANGELOG.md. This file follows the Keep a Changelog format and uses Semantic Versioning.

1
2
3
4
5
6
7
8
9
## [1.1.0] - 2026-01-26

### Added
- Resource limits and requests for all containers

### Changed
- Updated default memory limit to 512Mi

[1.1.0]: https://gitlab.com/project/compare/1.0.9...1.1.0

Available categories:

  • Added: new features
  • Changed: changes to existing features
  • Fixed: bug fixes
  • Removed: removed features
πŸ“Œ Important

If a change introduces a breaking change, it must be:

  1. Clearly stated in the CHANGELOG
  2. Explained in the Merge Request description
  3. Accompanied by a migration guide if needed

Semantic Versioning

We strictly follow Semantic Versioning (MAJOR.MINOR.PATCH):

  • MAJOR: breaking changes (incompatible with previous versions)
  • MINOR: new backward-compatible features
  • PATCH: backward-compatible bug fixes

Example:

  • 1.0.9 β†’ 1.1.0: adding resource limits (backward-compatible) β†’ MINOR
  • 1.1.0 β†’ 2.0.0: changing the structure of values.yaml (breaking) β†’ MAJOR

Observed benefits

Since this workflow was set up, we have observed:

Quality

  • Zero undocumented breaking changes in 6 months
  • 90% fewer deployment errors related to the charts
  • Early detection of problems (before code review)

Collaboration

  • A clear process for all contributors (juniors and seniors)
  • More effective code reviews (focus on logic, not syntax)
  • Full traceability of changes through the CHANGELOG

Trust

  • The development teams trust the stability of the charts
  • Updates are less feared because the process is transparent
  • Rollbacks are easier thanks to clear versioning

Productivity

  • Less time spent on manual validation
  • Automation of repetitive tasks
  • Focus on added value rather than on checking syntax

Sequence diagram

Sequence diagram: the merge request pipeline runs the lint, the rendering, the schema validation and the tests, then the tag pipeline packages the chart and publishes it to the registry

Complete sequence diagram

Points of attention

A few lessons learned while setting this up:

Complete unit tests

Do not only test the nominal cases. Include:

  • The default configurations
  • Edge cases
  • The different combinations of values
  • Upgrade scenarios

CHANGELOG documentation

The CHANGELOG is the contract with users. It must be:

  • Complete: every change must be documented
  • Clear: understandable language, no unnecessary technical jargon
  • Actionable: for breaking changes, state what to do

Protecting the main branch

The protection of main must be strict:

  • No direct commits
  • Mandatory pipeline
  • Mandatory approval
  • The CHANGELOG must be up to date before the merge

Protected tags

Restrict tag creation to Maintainers to avoid accidental publications.

Communication

When a breaking change happens, even a well-documented one:

  • Notify the teams in advance
  • Provide a migration guide
  • Offer support during the transition

Future improvements

We are considering adding:

  • Non-regression tests: compare the manifests generated between versions with helm diff
  • Security scanning: integrate tools such as Trivy to detect vulnerabilities
  • Policy enforcement: use tools such as OPA or Kyverno to validate security policies
  • Deployment monitoring: track the adoption of new versions

Conclusion

Setting up a rigorous validation pipeline for our Helm charts has changed the way we work. The incident that pushed us to implement this workflow is now impossible thanks to the multiple levels of validation.

This process not only guarantees the technical quality of the charts, it also sets a clear contract with the development teams: they can trust the charts we provide, and when a breaking change is needed, it is clearly documented and communicated.

For an SRE team, providing a reliable platform is paramount. This workflow lets us keep that promise while keeping the ability to evolve and continuously improve our charts.

If you manage Helm charts for a Kubernetes platform, I strongly encourage you to set up a similar process. The initial investment quickly pays off through fewer incidents and better collaboration.


Useful resources:

Built with Hugo
Theme Stack designed by Jimmy