How do Helm charts work — Chart.yaml, values.yaml, templates?
Quick Answer
A Helm chart is a directory containing Chart.yaml for metadata, values.yaml for default configuration, and a templates directory with Go-templated Kubernetes manifests that are rendered at install time by merging user-supplied values with the defaults.
Detailed Answer
Imagine a Helm chart as a blueprint for a house. Chart.yaml is the title block that tells the city which house design this is and who the architect is. values.yaml is the options catalog where the buyer chooses paint color, countertop material, and number of bedrooms. The templates folder holds the actual construction drawings with placeholder labels that get filled in based on the buyer's selections. Together, these three pieces let you build the same house in any neighborhood with local customizations.
Chart.yaml is the identity card of every chart. It declares the chart name, version following Semantic Versioning, the appVersion tracking the underlying application release, a description, the chart type which is either application or library, and an optional list of dependencies. Dependencies allow a chart to pull in sub-charts, for example a payments-api chart can declare a dependency on a shared postgresql chart and a redis chart. Helm resolves these dependencies during helm dependency update, downloading them into the charts subdirectory. The version field controls chart distribution and rollback targets, while appVersion is purely informational and helps operators correlate a Helm release with the actual software version running inside containers.
The templates directory is where Kubernetes manifests live as Go template files. Helm uses the Go text/template engine augmented with the Sprig function library, giving you access to over one hundred helper functions for string manipulation, math operations, date formatting, dictionary handling, and cryptographic utilities. Inside a template, you access values through the dot context: .Values for user configuration, .Release for install-time metadata like release name and namespace, .Chart for Chart.yaml fields, and .Capabilities for cluster feature detection such as API versions available. Control structures like if, range, and with let you conditionally render blocks or iterate over lists. The special files _helpers.tpl and NOTES.txt serve distinct roles: _helpers.tpl defines reusable named templates invoked with the include function, while NOTES.txt renders post-install instructions displayed to the operator.
In production, teams maintain a base values.yaml with sane defaults and layer environment-specific overrides using the dash-f flag pointing to files like values-staging.yaml or values-production.yaml. Helm performs a deep merge of these files from left to right, with later files taking precedence. Individual keys can be further overridden with the dash-dash-set flag on the command line, which takes highest priority. This layered approach enables a single chart to serve dozens of environments without duplicating any template code. Organizations often store environment values files in a separate GitOps repository, keeping chart code and configuration concerns cleanly separated.
A frequently overlooked gotcha is whitespace management in templates. The Go template engine preserves whitespace literally, which means a misplaced template action can inject blank lines or incorrect indentation into the rendered YAML, causing Kubernetes to reject the manifest. Helm provides the dash trim marker, written as a hyphen inside the template delimiters, to chomp whitespace before or after an action. The toYaml function combined with nindent is the idiomatic way to inject multi-line YAML blocks at the correct indentation level. Always validate rendered output with helm template before committing changes, and run helm lint to catch schema violations, missing required values, and deprecated API versions early in the development cycle.