A Helm Chart is a package for Kubernetes: it bundles all the manifests an application needs to run, together with templates you adapt to your environment through a values.yaml. Kubernetes Helm plays the role of a package manager here, much like the one on your operating system: you pick a package, hand it a few values, and Helm turns that into finished Kubernetes objects in your cluster.
Anyone who has maintained a Deployment, a Service and an Ingress manifest by hand for the same application across three environments quickly sees why Helm caught on. I run production-grade Kubernetes clusters and have been rolling out applications through Helm Charts there for years, paired with GitOps through ArgoCD. You can find all my articles on running Kubernetes on the Kubernetes page.
What is a Helm Chart in Kubernetes?
Helm has three terms worth keeping apart. A Chart is the package itself: all the manifests and templates an application needs, bundled into a folder structure or a packaged archive. A Release is one concrete installation of that Chart in your cluster, you can install the same Chart several times and end up with several independent Releases, say for different customers or environments. A Repository, finally, is where Charts get stored and distributed, comparable to a package repository for your operating system.
The difference from a plain folder of YAML files lies in the templating: a Chart does not contain finished manifests, it contains templates with placeholders that Helm only turns into real Kubernetes manifests at rollout time, filled in with the values from your values.yaml. That is exactly what makes a Chart reusable without you having to maintain a separate copy of the manifests for every environment.
The anatomy of a Helm Chart: Chart.yaml, values.yaml, templates
Run helm create webshop and Helm generates a complete standard Chart with sample manifests for you. The structure looks the same at its core for every Chart:
webshop/
├── Chart.yaml
├── values.yaml
├── charts/
└── templates/
├── deployment.yaml
├── service.yaml
├── ingress.yaml
└── _helpers.tpl
Chart.yaml holds the name, version and description of the Chart, the metadata that uniquely identifies a Chart inside a repository:
apiVersion: v2
name: webshop
description: Chart for the webshop service
type: application
version: 0.1.0
appVersion: "1.4.0"
version is the version of the Chart itself, appVersion the version of the application inside it; the two are counted up independently of each other.
The templates folder holds the actual Kubernetes manifests, enriched with placeholders in double curly braces. An excerpt from the Deployment might look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-webshop
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: webshop
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
Everything inside the {{ }} gets evaluated by Helm before the rollout. .Release.Name is a built-in value and returns the Release's name, while .Values.replicaCount and .Values.image come from your values.yaml. The result is a perfectly ordinary Kubernetes manifest, Kubernetes itself never sees any of the template syntax. How you develop such a Chart yourself from scratch, including every template function and condition, is covered in full in Creating Your Own Helm Chart.
Installing, upgrading and rolling back a Helm Chart
A handful of Helm commands get you through the first steps. From inside your Chart's folder you roll it out for the first time with helm install, and every later update goes through helm upgrade:
| Command |
Effect |
helm install webshop . |
rolls out the Chart as a new Release called webshop |
helm upgrade --install webshop . |
updates the Release, or creates it if it does not exist |
helm history webshop |
shows every past revision of the Release |
helm rollback webshop 2 |
resets the Release to revision 2 |
helm uninstall webshop |
removes the Release and everything that belongs to it |
In pipelines I use helm upgrade --install almost exclusively, because then I never have to figure out whether a Release already exists. Every upgrade bumps the Release's revision number by one, and that history is exactly what makes helm rollback possible: if a rollout fails or a new value turns out wrong, one single command jumps you back to the last working state, without you having to keep the old manifests around anywhere.
Overriding values: your own configuration per environment
A Chart always ships a values.yaml with sensible defaults so it works right out of the box. For your own adjustments you override those values without touching the Chart itself, either straight from the command line or with an additional values file:
helm upgrade --install --set image.tag=c3f1a9d webshop .
helm upgrade --install -f values-base.yaml -f values-prod.yaml webshop .
You never pass the Chart's own values.yaml with -f, it is always the base, and anything you supply through --set or your own file layers on top of it. Pass several values files and, for anything that overlaps, the file named last always wins. That lets you keep defaults, environment-specific values, and, say, team- or region-specific values cleanly separated in their own files instead of stuffing one values.yaml full of comments. For dev, staging and prod I typically keep one lean values file each, holding only the differences from the default, such as the replica count or the image tag.
Rolling out Helm Charts through GitOps instead of by hand
Running helm install and helm upgrade straight from your own command line is fine for learning and testing, in production I prefer a different path: GitOps. Instead of a person or a pipeline running the Helm command directly against the cluster, an ArgoCD Application declares which Chart with which values should be rolled out, and ArgoCD renders and reconciles that on its own against the desired state stored in Git:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: webshop
spec:
project: default
source:
repoURL: https://charts.example.com
chart: webshop
targetRevision: 0.1.0
helm:
values: |
replicaCount: 3
destination:
server: https://kubernetes.default.svc
namespace: webshop
Two details are worth a look: project is mandatory in an Application, and the values sit directly in the values block here, because valueFiles only resolves files inside the Chart when the source is a Chart repository. If your Chart lives in Git instead, you can put a values file next to it there and reference it.
The difference is more than cosmetic: with helm install from the command line, you can never be entirely sure what is actually running in the cluster unless someone logs every command. With GitOps, Git is the single source of truth, every change to the Chart version or the values goes through a commit, and ArgoCD continuously reconciles the cluster against Git. How GitOps with ArgoCD works in detail, beyond Helm as well, is covered in GitOps with ArgoCD: Deploy from Git.
Handling secrets in Helm Charts properly
The obvious but wrong path is to put passwords and credentials straight into a values.yaml and commit that to the repository. That puts every secret into the version history in plain text, permanently, even if you delete the line again later. In my clusters I encrypt values files that hold sensitive values before they go into the repository, and the pipeline only decrypts them right before the Helm rollout. That keeps Git as the source of truth without a secret ever landing there unencrypted. Alternatively, many teams rely on external secret stores that Kubernetes pulls values from at runtime, instead of routing them through Helm at all.
Frequently asked questions
What is the difference between Helm and Kustomize?
Helm packages an application as a versioned Chart with a templating engine and values, while Kustomize patches plain Kubernetes YAML without its own template language, usually through overlays for different environments. This post is about how a Chart is built and how you work with it; the decision between the two tools, with selection criteria, is in Kustomize vs Helm: When to Use Which?.
Do I need Helm if I already use ArgoCD?
Not necessarily, ArgoCD can also roll out plain YAML or Kustomize overlays. But once you start installing third-party applications from public Charts or want to version your own applications across many environments, Helm and ArgoCD complement each other well: Helm supplies the package and the templating logic, ArgoCD handles the GitOps rollout around it.
How do I manage secrets in Helm Charts?
Never put plaintext passwords into a values.yaml that gets committed to the repository. Encrypt any values files that hold sensitive data before the commit and decrypt them only at rollout time, or pull secrets from an external secret store at runtime instead of passing them through Helm.
How do I roll back a Helm release?
helm history <release> shows every past revision, and helm rollback <release> <revision> resets the Release to one of them. That only works as long as Helm still has that revision stored in its history inside the cluster.
Where to go next
The next step is developing a Chart of your own instead of only installing finished ones: template functions, conditions and a clean values structure are covered step by step in Creating Your Own Helm Chart. If you are unsure whether Helm is even the right tool for your use case, Kustomize vs Helm: When to Use Which? helps you decide, and for a production rollout through Git, GitOps with ArgoCD: Deploy from Git is worth reading.
My suggestion for getting started: install a standard Chart with helm create, roll it out into a test namespace, change one value in the values.yaml afterward, and watch with helm upgrade and helm rollback how revisions behave in the cluster.
In full detail, with every template function, condition and the anatomy of building your own Charts, which I could only sketch here, this is covered in chapter 9 of my Kubernetes Practical Guide (Rheinwerk Computing). The full command reference is in the official Helm documentation, and the details of ArgoCD's Helm integration are in its documentation on Helm sources.