Sign in
← Kubernetes: Kustomize and Helm
6 min · medium · Lesson 7 of 9

Helm: chart, release, and the first install

What a chart is made of, what Helm 3 stores in the cluster after install, and how to configure a release with --set.

Kustomize layers edits on top of finished manifests. Helm comes from the other side: its manifests are templates with substitutions, and the specific values are supplied separately. One command deploys the whole application.

Why this matters

An application in Kubernetes rarely consists of one file: a Deployment, a Service, a ServiceAccount, sometimes an Ingress and a ConfigMap. Installing it into three environments with a different replica count and different tags by hand is awkward. Helm packages these manifests together, and pulls out everything that differs between installs into values. Ready-made applications are distributed the same way: databases, ingress controllers, and monitoring get installed from someone else's charts.

Which Helm

Two branches are current: Helm 3 and Helm 4 (released November 2025). This lesson's commands work the same in both.

If you run into an article about Tiller, that's Helm 2. There, a server component called Tiller with broad permissions lived in the cluster. With Helm 3 it's gone: the client talks to the API server directly, under the same permissions as your kubectl, and reads the same kubeconfig.

Chart and release

A chart is a package for Kubernetes: a folder with manifest templates, default values, and metadata. The minimal set of files inside:

myapp/
  Chart.yaml       # chart name, chart version, app version
  values.yaml      # default values
  templates/       # manifest templates with substitutions
  charts/          # nested dependency charts
  .helmignore

A release is an installed instance of a chart in a specific namespace. One chart can be installed as many times as you want under different release names, and those will be independent installs.

Where charts come from

The usual path: a chart repository. You add it once, update the index, and search for the chart you need:

helm repo add podinfo https://stefanprodan.github.io/podinfo
helm repo update
helm search repo podinfo
NAME           	CHART VERSION	APP VERSION	DESCRIPTION
podinfo/podinfo	6.15.0       	6.15.0     	Podinfo Helm chart for Kubernetes

Second path: a local folder. A chart skeleton is generated with one command:

helm create myapp

helm create puts a working default chart into the myapp folder: templates for a Deployment, Service, ServiceAccount, Ingress, HTTPRoute, HorizontalPodAutoscaler, a test Pod, and a values.yaml with settings. No network is needed for this, so it's a convenient option for studying how a chart is put together.

Installing a release

helm install web ./myapp -n demo --create-namespace

web here is the release name, ./myapp is the path to the chart, -n demo is the namespace. Without --create-namespace, Helm won't create the namespace itself and will fail if it doesn't exist.

See what you got:

helm list -n demo
helm list -A
helm status web -n demo

Under the hood: ordinary objects

Helm renders templates into ordinary manifests and applies them through the same API. No separate entity for the application shows up in the cluster, everything is visible through plain kubectl. A chart from helm create puts a label with the release name on the objects:

kubectl get deploy,svc -n demo -l app.kubernetes.io/instance=web

Helm keeps track of the release itself. It writes the state of each revision into a Secret in the same namespace, named like sh.helm.release.v1.web.v1, where web is the release name and the last v1 is the revision number. That's where history, rollbacks, and the answer to which objects belong to the release come from. That's why you remove a release with helm uninstall, not kubectl delete: otherwise the Secret stays behind and Helm keeps thinking the release is installed.

The exact list of applied manifests is shown by:

helm get manifest web -n demo

Configuring through values

You don't touch the templates. You change the values the templates rely on. For example, with a flag right in the command:

helm install web ./myapp -n demo \
  --set replicaCount=3 \
  --set image.repository=nginx \
  --set image.tag=1.27.3-alpine

A dot in --set means nesting: image.repository lands in image: {repository: ...}. What values a chart accepts at all:

helm show values ./myapp

--set guesses the type of a value. true and false become booleans, an integer like 1042 becomes a number, and 1.25 or 1.27.3-alpine stay strings. A number in a field where Kubernetes expects a string (a label value, an environment variable) is rejected by the API. For such values there's --set-string: it passes everything after = as a string.

Check the result before installing:

helm template web ./myapp --set replicaCount=3
helm install web ./myapp -n demo --dry-run=server

helm template renders locally and doesn't ask the cluster anything. helm install --dry-run=server reaches out to the cluster, but creates nothing in it. Write the flag with a value: plain --dry-run in Helm 4 means client and prints a warning that the form without a value is deprecated.

What usually breaks

A release name is unique within a namespace. Running helm install web again in the same namespace gives an error about the name already being taken. The same name in a different namespace gets taken without complaint.

A forgotten -n puts the release into the namespace from the current kubeconfig context, not the one you had in mind. Then helm list in the namespace you wanted shows nothing, and it looks like the install didn't go through. helm list -A shows where the release actually ended up.

A comma in --set separates value pairs, and a dot means nesting. If you need a comma or a dot inside the value itself, escape them with a backslash: --set 'annotations.example\.com/id=a\,b'.

A failed install still leaves the release behind, in status failed. A repeat helm install with the same name runs into the taken name; you need helm uninstall first.

A manual edit of objects created by Helm through kubectl edit isn't reflected in the release state. The next helm upgrade rebuilds the manifests from the chart and values and brings everything back to how it was.

What you'll need for the tasks

Homework

Practice the theory on your own cluster: 1 step with a check. Set up the cluster with the Kubernetes locally track, the tasks live in the k8s-practice repository.

./run.sh start k8s-helm-install     # set up the starting state
./run.sh verify k8s-helm-install    # check the current step

In short

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson k8s-packaging-07-helm-install