Theory
Reading time: ~20 minutes · Prerequisites: Lesson 01 (Introduction to Helm), basic Kubernetes familiarity
1. Scaffolding a Chart with helm create
Every Helm chart starts as a directory containing a specific set of files. While you could create these files by hand, the helm create command generates a fully functional, best-practice chart in seconds. This scaffolded chart deploys an nginx web server with a Deployment, Service, and optional Ingress — a working application you can immediately install and then customize.
helm create myapp
This single command produces the following directory tree:
myapp/
Chart.yaml
values.yaml
charts/
templates/
deployment.yaml
service.yaml
ingress.yaml
hpa.yaml
serviceaccount.yaml
_helpers.tpl
NOTES.txt
tests/
test-connection.yaml
.helmignore
Each file serves a distinct purpose. The rest of this reading walks through every component in detail, explaining not just what each file contains but why it exists and how the pieces connect during rendering and deployment.
Tip: Always Start with helm create
Even experienced chart authors use helm create as a starting point. The generated templates include proper labels, selectors, helper functions, and conditional blocks that are easy to forget when writing from scratch. Delete what you do not need rather than building from zero.
2. Chart.yaml — The Chart’s Identity
The Chart.yaml file is the only strictly required file in a Helm chart (besides at least one template or dependency). It declares the chart’s metadata: its name, version, and description. Helm reads this file to identify the chart, resolve dependencies, and display information in search results.
apiVersion: v2
name: myapp
description: A Helm chart for deploying my application
type: application
version: 0.1.0
appVersion: "1.0.0"
Required Fields
- apiVersion — Must be
v2for Helm 3 charts. Helm 2 usedv1, which lacked support for library charts and thetypefield. If you seev1, the chart was written for Helm 2 and may need migration. - name — The chart name. This should match the directory name and is used in release naming, repository indexing, and template helpers. Use lowercase and hyphens (e.g.,
my-app). - version — The chart’s own version, following Semantic Versioning (SemVer). Bump this every time you change anything in the chart — templates, values, dependencies, or metadata. Helm repositories use this version to determine whether an update is available.
Optional but Important Fields
- appVersion — The version of the application the chart deploys (e.g., the Docker image tag). This is purely informational and does not affect Helm’s behavior, but it appears in
helm listoutput and helps operators understand what is running. - description — A one-line summary displayed by
helm searchand artifact registries. - type — Either
application(the default, produces installable releases) orlibrary(provides reusable template helpers to other charts but cannot be installed directly).
version vs. appVersion
These two versions are independent. You might release chart version 0.3.0 that still deploys app version 1.0.0 because you only changed the Ingress template. Conversely, bumping the app to 2.0.0 without touching templates still warrants a chart version bump (e.g., 0.3.1) so consumers know something changed.
3. values.yaml — Configuration Defaults
The values.yaml file contains the default configuration for the chart. Every key defined here becomes accessible in templates via the .Values object. Users override these defaults at install time using --set flags or -f value files, without modifying the chart itself.
replicaCount: 1
image:
repository: nginx
pullPolicy: IfNotPresent
tag: ""
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
How Values Flow into Templates
Templates reference values using Go template syntax. For example, {{ .Values.replicaCount }} resolves to 1 unless overridden. Nested values use dot notation: {{ .Values.image.repository }} yields nginx. This separation of configuration from templates is what makes Helm charts reusable — the same chart can deploy to development, staging, and production with different value files.
# Override a single value
helm install myrelease ./myapp --set replicaCount=3
# Override with a custom values file
helm install myrelease ./myapp -f production-values.yaml
# Combine both (--set takes precedence)
helm install myrelease ./myapp -f production-values.yaml --set image.tag=v2.1.0
Value Precedence
When multiple sources provide the same key, Helm merges them with a clear priority order: --set flags override -f file values, which override the chart’s built-in values.yaml. Within multiple -f files, the last file wins.
4. The templates/ Directory
The templates/ directory holds Go-templated Kubernetes manifests. When Helm renders a chart, it processes every .yaml and .tpl file in this directory (recursively), substituting values and evaluating control structures to produce valid Kubernetes YAML.
Core Template Files
- deployment.yaml — Defines the Deployment resource: replica count, container image, ports, probes, resource limits, and volume mounts. This is typically the most important template.
- service.yaml — Exposes the Deployment via a Kubernetes Service. The scaffolded version supports ClusterIP, NodePort, and LoadBalancer types controlled by
.Values.service.type. - ingress.yaml — Conditionally rendered (wrapped in
{{- if .Values.ingress.enabled }}). When enabled, it creates an Ingress resource for HTTP routing with host-based and path-based rules. - hpa.yaml — Horizontal Pod Autoscaler, also conditionally rendered. Configures automatic scaling based on CPU or memory utilization.
- serviceaccount.yaml — Creates a ServiceAccount for the pods, allowing fine-grained RBAC control.
_helpers.tpl — Reusable Template Partials
Files prefixed with an underscore are not rendered as Kubernetes manifests. Instead, _helpers.tpl defines named templates (partials) that other templates include. The scaffolded helpers provide:
myapp.name— The chart name, truncated to 63 characters (Kubernetes label limit).myapp.fullname— Combines the release name and chart name for unique resource naming.myapp.labels— Standard Kubernetes labels (app.kubernetes.io/name, version, managed-by, etc.).myapp.selectorLabels— The subset of labels used in Deployment selectors.
# Using a helper in deployment.yaml
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
NOTES.txt — Post-Install Instructions
The NOTES.txt file is a special template that does not produce a Kubernetes resource. Instead, its rendered output is displayed to the user after helm install and helm upgrade. Use it to provide connection instructions, port-forward commands, or next steps.
1. Get the application URL by running:
{{- if .Values.ingress.enabled }}
http://{{ (index .Values.ingress.hosts 0).host }}
{{- else if eq .Values.service.type "ClusterIP" }}
kubectl port-forward svc/{{ include "myapp.fullname" . }} 8080:{{ .Values.service.port }}
Visit http://127.0.0.1:8080
{{- end }}
Because NOTES.txt is a template, it can adapt its output based on the user’s configuration — showing different instructions depending on whether Ingress is enabled, the service type, or the namespace.
5. Supporting Files
charts/ Directory
The charts/ directory holds dependency charts (subcharts). When you declare dependencies in Chart.yaml and run helm dependency update, Helm downloads the dependency archives into this directory. For a simple chart with no dependencies, this directory is empty.
.helmignore
Similar to .gitignore, the .helmignore file lists patterns for files that should be excluded when packaging the chart with helm package. The default includes common entries like .git/, *.swp, and .DS_Store.
6. The Release Lifecycle
Helm manages the complete lifecycle of a release — the running instance of a chart in a cluster — through four primary commands.
helm install
Creates a new release. Helm renders all templates with the provided values, submits the resulting manifests to the Kubernetes API, and stores the release metadata (including the rendered manifests and values) as a Secret in the target namespace.
helm install myrelease ./myapp --namespace production --create-namespace
helm upgrade
Updates an existing release. Helm re-renders the templates with new values or a new chart version, computes a diff against the current state, and applies only the changes. Each upgrade increments the revision number.
helm upgrade myrelease ./myapp --set image.tag=v2.0.0
helm rollback
Reverts a release to a previous revision. Importantly, rollback creates a new revision rather than deleting history. Rolling back from revision 3 to revision 1 produces revision 4 with the configuration from revision 1.
helm rollback myrelease 1
helm uninstall
Removes all Kubernetes resources associated with the release and deletes the release metadata. After uninstall, the release name becomes available for reuse.
helm uninstall myrelease --namespace production
7. Inspecting Releases
Beyond the lifecycle commands, Helm provides inspection commands that are essential for debugging and auditing.
| Command | Purpose |
|---|---|
helm list | List all releases in the current namespace (add -A for all namespaces) |
helm status <release> | Show the current state, revision, namespace, and NOTES output |
helm history <release> | Display all revisions with timestamps, status, and descriptions |
helm get manifest <release> | Print the rendered Kubernetes manifests for the current revision |
helm get values <release> | Show the user-supplied values (add -a for all computed values) |
helm get notes <release> | Re-display the NOTES.txt output |
These commands query the release Secrets stored in the cluster, not the local chart directory. This means you can inspect a release even if you no longer have the chart source on your machine.
Tip: Debug Before Installing
Use helm template ./myapp to render templates locally without contacting the cluster, or helm install --dry-run --debug to simulate an install and see the rendered output. Both are invaluable for catching template errors before they reach your cluster.
8. Putting It All Together
A typical workflow for your first chart looks like this:
- Scaffold — Run
helm create myappto generate the chart structure. - Customize — Edit
values.yamlto set your image, ports, and resource limits. Modify templates if needed. - Validate — Run
helm template ./myappto verify the rendered output looks correct. - Install — Deploy with
helm install myrelease ./myappand check the NOTES output. - Iterate — Make changes and run
helm upgrade. If something breaks,helm rollback. - Inspect — Use
helm status,helm history, andhelm getto understand the release state. - Clean up — When done,
helm uninstallremoves everything.
This cycle — create, install, upgrade, rollback, inspect, uninstall — is the foundation of all Helm operations. Every advanced topic in later lessons (dependencies, hooks, testing, CI/CD integration) builds on this core lifecycle.