Helm Charts
The atomic packaging unit of Helm. A chart is a directory of files that describes a set of Kubernetes resources using Go templates, default values, and metadata. Charts turn complex multi-resource applications into reusable, versioned, and configurable packages. Synthesized from Helm Zero to Hero by @AbhishekVeeramalla.
Chart Directory Structure
A chart is a filesystem tree with a mandatory top-level Chart.yaml and a templates/ directory:
myapp/
├── Chart.yaml # Chart metadata and dependencies
├── values.yaml # Default configuration values
├── values.schema.json # Optional JSON schema for values validation
├── charts/ # Sub-charts (dependencies)
├── templates/ # Kubernetes manifest templates
│ ├── _helpers.tpl # Named template helpers
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ └── NOTES.txt # Post-install notes shown to user
├── templates/tests/ # Test manifests (run by helm test)
└── README.md # Human-readable documentation
Every file in templates/ is rendered through Go’s text/template engine. Files starting with an underscore (_) are not rendered as standalone manifests — they are intended for inclusion via {{ include }} or {{ template }}.
Chart.yaml — Metadata and Dependencies
apiVersion: v2
name: myapp
description: A Helm chart for the MyApp microservice
type: application # or "library" for reusable helper charts
version: 1.2.0 # chart version (follows SemVer)
appVersion: "2.5.1" # the application version this chart deploys
keywords:
- web
- microservice
home: https://github.com/org/myapp
sources:
- https://github.com/org/myapp
maintainers:
- name: DevOps Team
email: [email protected]
dependencies:
- name: postgresql
version: 12.0.0
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
tags:
- databaseapiVersion: v2is required for Helm 3 charts (v1 is Helm 2 legacy)versionis the chart’s own SemVer — incremented when templates or defaults changeappVersionis the application being deployed — useful for correlating chart releases with application releasesdependenciesdeclares sub-charts that Helm fetches and installs alongside the parent
values.yaml — Default Configuration
values.yaml is the contract between chart author and chart consumer. It declares every tunable parameter with sensible defaults:
replicaCount: 2
image:
repository: nginx
pullPolicy: IfNotPresent
tag: "1.27"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: "nginx"
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: Prefix
tls: []
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80Best practices for values design:
- Flatten logically: group related settings under a key (
image.repository, notimagerepository) - Use booleans for features:
ingress.enabled: falselets consumers opt in cleanly - Keep secrets out: never put credentials in default values; require consumers to inject via external Secret mechanisms
- Document in README: every top-level key should have a one-line explanation
Templating with Go Templates
Helm manifests are Go templates. The template engine injects values and provides helper functions.
Built-in Objects
| Object | Description |
|---|---|
.Values | Contents of values.yaml plus CLI overrides |
.Release | Information about the release: .Name, .Namespace, .Revision, .IsUpgrade, .IsInstall |
.Chart | Contents of Chart.yaml: .Name, .Version, .AppVersion |
.Capabilities | Cluster capabilities: .KubeVersion, .APIVersions |
.Template | Current template being executed: .Name, .BasePath |
Common Template Patterns
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
resources:
{{- toYaml .Values.resources | nindent 12 }}Key syntax notes:
{{ ... }}outputs the result{{- ... }}strips leading whitespace (critical for YAML validity)| nindent 4indents a multi-line string by 4 spaces| default .Chart.AppVersionprovides a fallback if no tag is specifiedtoYamlserializes an object as YAML (commonly used forresources,nodeSelector,tolerations)
Named Templates in _helpers.tpl
The _helpers.tpl file defines reusable snippets:
{{/*
Expand the name of the chart.
*/}}
{{- define "myapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "myapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}Using named templates keeps manifests DRY and ensures consistent naming across Services, Deployments, and Ingresses.
Chart Dependencies (Sub-charts)
Complex applications often need databases, caches, or message brokers. Helm models these as dependencies declared in Chart.yaml:
dependencies:
- name: redis
version: 17.x.x
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
- name: postgresql
version: 12.x.x
repository: https://charts.bitnami.com/bitnami
alias: dbHelm downloads dependencies into the charts/ directory during helm dependency update. The condition field allows consumers to toggle sub-charts via values.yaml:
redis:
enabled: true
architecture: standalone
db:
enabled: falseThe alias field renames the dependency’s values namespace — useful when you need two instances of the same chart (e.g., two databases).
Creating a Chart from Scratch
# Scaffold a new chart
helm create myapp
# Customize templates, values, and metadata
# Then validate
helm lint ./myapp
# Preview rendered output
helm template myapp ./myapp
# Install for real
helm install myapp ./myappThe helm create scaffold includes a working NGINX example. Replace the templates and values to match your application.
Packaging and Distribution
Charts are distributed as .tgz archives with a SHA256 checksum:
# Package the chart
helm package myapp # produces myapp-1.2.0.tgz
# Generate repository index
helm repo index . --url https://example.com/charts
# The resulting index.yaml looks like:
# apiVersion: v1
# entries:
# myapp:
# - apiVersion: v2
# appVersion: 2.5.1
# version: 1.2.0
# urls:
# - https://example.com/charts/myapp-1.2.0.tgz
# digest: sha256:abc123...Host index.yaml and .tgz files on any static web server (GitHub Pages, S3, GCS, Artifactory, Nexus). Consumers add the repository with helm repo add and install charts by name.
Chart Testing
Helm supports verification tests that run as short-lived Jobs after installation:
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "myapp.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "myapp.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: NeverRun with helm test myapp. These tests validate that the deployed application is actually reachable and healthy.
Related Pages
- Helm — Main Helm concept page: architecture, commands, and ecosystem
- Helm Release Management — Installing, upgrading, and rolling back chart instances
- Kubernetes Ingress — Ingress controller charts and annotations
- Kubernetes ConfigMaps and Secrets — Externalizing configuration for Helm charts
- Kubernetes Namespaces — Chart release scoping
- GitOps Security — Chart provenance, signing, and repository trust
- Abhishek Veeramalla — Creator of the Helm Zero to Hero course
Tags: helm charts kubernetes go-templates templating devops packaging cncf