WSS
On this page
Containers & OrchestrationRequiredUpdated

Helm chart structure

The standardized directory and file layout required to package, configure, and deploy applications as Helm charts on Kubernetes.

What it is

Helm is a package manager for Kubernetes. Just as apt manages Debian packages or npm manages Node.js packages, Helm manages Kubernetes applications.

A Helm “Chart” is the packaging format. It is fundamentally a collection of files organized in a specific, strictly defined directory structure. This structure contains a mix of static metadata, default configuration values, and Golang text templates that generate valid Kubernetes YAML manifests when rendered.

Why it matters

Kubernetes YAML is verbose and statically defined. If you want to deploy the same application to three different environments (Dev, Staging, Prod), you would typically need to copy-paste thousands of lines of YAML and manually change the image tags, replica counts, and ingress hostnames in each copy.

Helm solves this by abstracting the YAML into templates. The Helm Chart structure enforces a clear separation of concerns: the templates/ directory holds the underlying infrastructure logic, while the values.yaml file exposes a clean, user-friendly API for configuring the deployment. This standardization allows operations teams to publish complex, reusable infrastructure packages that developers can consume with a single command.

How to implement

A valid Helm chart requires, at minimum, a specific directory structure. When you run helm create my-app, Helm scaffolds this default structure:

my-app/
├── Chart.yaml          # A YAML file containing information about the chart
├── values.yaml         # The default configuration values for this chart
├── charts/             # A directory containing any charts upon which this chart depends
├── templates/          # A directory of templates that, when combined with values, generate valid Kubernetes manifest files
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl    # A place to put template helpers that you can re-use throughout the chart
│   └── NOTES.txt       # A plaintext file containing short usage notes printed after deployment

1. Chart.yaml (The Metadata)

This file defines the chart’s identity. It must contain apiVersion (usually v2), name, and version.

apiVersion: v2
name: nginx-webserver
description: A basic NGINX web server deployment
version: 1.0.0
appVersion: "1.21.0"

2. values.yaml (The API)

This file defines the default parameters users can override when they install the chart.

replicaCount: 3
image:
  repository: nginx
  tag: "1.21.0"

3. templates/ (The Logic)

This directory contains Go templates. Helm injects the data from values.yaml into these templates.

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Chart.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: web
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

Common mistakes

Hardcoding Values in Templates

The primary purpose of a Helm chart is reusability. If a developer hardcodes a namespace (namespace: production) or a specific cluster hostname directly into templates/ingress.yaml, the chart can never be deployed to the staging environment. Every environmental variable must be extracted into values.yaml and injected into the template using {{ .Values.hostname }}.

Logic Bleed in Templates

Helm uses the Go templating engine, which supports if/else statements and loops. Complex charts often end up with templates containing 500 lines of deeply nested conditional logic, making the YAML completely unreadable and impossible to debug. If a template requires massive conditional blocks, it usually indicates the chart is trying to do too many things and should be split into smaller, composable sub-charts (placed in the charts/ directory).

Ignoring NOTES.txt

When a user installs a chart via helm install, the contents of templates/NOTES.txt are rendered and printed to their terminal. Failing to populate this file with clear instructions on how to access the newly deployed application (e.g., printing the generated LoadBalancer IP or internal DNS name) forces the user to manually dig through kubectl commands to find their application.

Verification

To verify that your Helm chart structure is valid and that your templates compile successfully without actually deploying anything to a cluster, use the helm lint and helm template commands.

# Verify the chart structure and metadata are valid
helm lint ./my-app

# Render the templates locally and output the final Kubernetes YAML to the console
# This is crucial for verifying that loops and conditionals rendered correctly
helm template ./my-app --set replicaCount=5

Related topics

Sources & further reading