KubeSchool
Back to the Kubernetes primer
KubeSchool · brought to you by Portainer

How Helm works, charts, values and releases

Congratulations on making it this far, and now we continue the lesson by taking you slightly deeper into the rabbit hole... Helm.

Helm is the package manager for Kubernetes, and once you can picture how a chart turns into running software, the whole tool clicks into place. It keeps to the parts that make the tool make sense, not an exhaustive tour of every flag and function.

6 chapters Written for Helm 4 kubeschool.portainer.io
00

What Helm is

Deploying an application to Kubernetes by hand means writing a pile of YAML. You need a Deployment, a Service, probably a ConfigMap, maybe an Ingress and a few more objects, and then you copy and tweak all of it for every environment and every app you run. It works, but it gets tedious fast and it goes wrong easily, because the interesting differences between environments are buried in a lot of near-identical text.

Helm is the answer to that, and it is the package manager for Kubernetes. In the same way your laptop uses something like Homebrew or apt to install software with a single command, Helm installs whole applications onto a cluster from a reusable package called a chart, with the settings you actually care about pulled out front where you can change them. Instead of hand-editing twenty files, you install a chart and pass in a handful of values.

The rest of this comes down to a short story you will see repeated. You package an application as a chart, you install that chart to create a running release, you change and upgrade that release over time, and the charts themselves come from repositories you can pull from. Everything that follows is just those few ideas in more detail.

01

Charts, values, and templates

The whole of Helm rests on three things that fit together, and if you get these you get Helm. A chart is the package: a folder of files that describes an application as a set of Kubernetes manifests, except the parts that tend to change are left as blanks. A values file is where those blanks get their settings, the knobs a user is meant to turn, like how many replicas to run, which image version to use, or how much memory to request. And templates are the manifests with the blanks still in them, written in Go's templating syntax, so a line like replicas: {{ .Values.replicas }} is a manifest waiting for a number. When you install, Helm pours your values into the blanks in the templates, and out comes ordinary Kubernetes YAML.

From your settings to real YAML values.yaml replicas: 3 a template replicas: {{ .Values.replicas }} real YAML replicas: 3 fills in renders
Your settings drop into the blanks, and out comes ordinary Kubernetes YAML.

What is in a chart

A chart is just a folder with a known layout. Chart.yaml holds the metadata, the chart's name, version, and description, and values.yaml holds the default settings. The templates/ folder holds the templated manifests, along with a NOTES.txt that prints friendly usage tips after an install and a _helpers.tpl for snippets shared across templates. A charts/ folder holds any other charts this one depends on. And a crds/ folder holds any custom resource definitions the chart ships.

CRDs, yes you can install them with Helm

A CRD is just another kind of Kubernetes object, so there is nothing exotic about installing one with Helm. A chart can ship its own custom resource definitions and stand up a brand-new object type as part of the very same install that deploys the app, no differently from the way it creates a Deployment or a Service.

CRDs are installed once, not upgraded

Helm installs the CRDs in a chart's crds/ folder on the very first install and then leaves them alone, never upgrading or deleting them on later runs. So if a chart later ships a newer version of a CRD, Helm will not apply it for you, and updating it is a step you do by hand, which is the surprise that catches almost everyone at least once.

Changing the settings

You rarely edit a chart itself, and instead override its values from the outside, either with a whole file of your own using -f my-values.yaml, or with individual settings using --set replicas=3. The order they apply in matters, and it trips people up, so hold onto it: the chart's own values.yaml is the baseline, a file passed with -f layers on top of that, and anything set with --set beats both.

Charts that build on other charts

Most real charts are not one thing, they are several. A web app might need a database and a cache running alongside it, and rather than cram all of that into one giant chart, Helm lets a chart pull in other charts as ready-made building blocks. A chart lists the other charts it needs in its Chart.yaml, those needed charts are called its subcharts, and they sit in the charts/ folder. When you install the top chart, Helm installs it and all of its subcharts together as a single release, so one command can stand up the app, its database, and its cache in one go.

One chart, built from others myapp chart your appits own templates postgresqla subchart redisa subchart helm install one release app, database, and cache together
The top chart pulls in subcharts, and a single install brings up all of them.

Configuring a subchart is the one part that catches people, so here is the trick. Because a subchart is its own chart with its own values, you set its options in a section of your values file named after it. If the database subchart is called postgresql, then anything you put under a postgresql key in your values is handed to that subchart, while the app's own settings sit at the top level next to it. Once you see that the values file is simply divided into a section for each chart, the nesting stops being mysterious.

Whitespace will fight you at first

Templates are just text, and YAML cares deeply about indentation, so a stray space can break a render in confusing ways. Getting the whitespace controls right, the {{- and -}} that trim it, is a small rite of passage for everyone who writes their first chart. You are not doing it wrong, it just takes a few goes.

02

How an install actually works

You normally use Helm in two passes, a dry run first, which rehearses an install and shows you exactly what it would do without changing anything on the cluster, and then the real install, which actually deploys it. Both drive the same machinery, so the clearest way in is to follow a real install through first, then see how a dry run simply stops short of the steps that touch the cluster.

When you run helm install myapp ./mychart, a clear sequence of steps happens, and notably none of it involves a Helm server, because there is not one.

First, Helm reads the chart and merges your values with the chart's defaults. Second, it renders the templates on your own machine into a plain set of Kubernetes manifests, and at this point nothing has touched the cluster yet. Third, it sends those manifests to the Kubernetes API server using your credentials, and Kubernetes creates the objects, with Helm 4 leaning on Kubernetes' own server-side apply to work out the changes. Fourth, Kubernetes does its usual job of making those objects real, and Helm can wait and watch until they report healthy. Fifth, Helm records what it did as a release, stored right inside the cluster as a Secret, holding the rendered manifests, the values used, and a revision number.

A dry run first, then the real install ON YOUR MACHINE ON THE CLUSTER render the chart chart + your values with --dry-run · optional the cluster checks it, and nothing is created the real install apply to cluster objects created release saved
The render is the same either way, but a dry run only checks and changes nothing, while the real install applies it and records the release.

That saved record is the bit that pays off later, because it is what every upgrade and rollback quietly leans on. Since Helm kept exactly what it deployed and with which settings, it can compare, upgrade, and undo later without any guesswork. A release is simply a named, versioned installation of a chart into a namespace, and the cluster itself remembers it.

How a dry run actually works

A dry run is that same flow with the last steps switched off. Helm still reads the chart, merges your values, and renders the templates into manifests, just as a real install would, but then it stops before anything is created. At its simplest it shows you the manifests it would have applied, so you can read exactly what is about to be deployed. You can also ask it to hand those manifests to the cluster for a check, where the cluster runs the same validation it would run for real and tells you whether they would be accepted, still without creating anything. Either way the cluster is left exactly as it was, which is what makes a dry run a safe rehearsal you can repeat as often as you like.

Previewing is not installing

Both helm template and helm install --dry-run let you preview an install, and neither one deploys anything. It is easy to run a clean preview, see tidy YAML or a green dry run, and assume the app is up when nothing has actually been created. A preview is only a rehearsal, so look for the real objects in the cluster before you trust that anything is running.

Hooks, the extra steps around an install

Sometimes a chart needs to do something at a precise moment, like run a database migration just before an upgrade or load seed data right after the first install, and that is what hooks are for. A hook is an ordinary Kubernetes resource, usually a Job, marked with a special annotation that tells Helm exactly when to run it, whether that is pre-install, post-upgrade, pre-delete, or one of the other points in a release's life. Helm runs that resource at the named moment and waits for it to finish before carrying on with everything else. Unlike the Deployment or Service in a chart, a hook is not meant to stick around as part of the running app, it does its one job and gets out of the way. This is how charts handle the awkward tasks that have to happen in a particular order, the migrations, the backups, the one-off setup, without anyone running them by hand at the console.

03

Releases, upgrades, and rollbacks

Everything after the first install is built on that release record. Each time you change a value or move to a newer chart and run helm upgrade, Helm renders the new version, applies the difference to the cluster, and saves a new revision, version 2, then version 3, and so on. Running helm history myapp shows you the whole trail of what was deployed when.

Every change is a saved revision Revision 1first install Revision 2after an upgrade Revision 3current upgrade upgrade helm rollback 2
Rolling back is just pointing Helm at an older revision it already saved.

Because every past revision is still stored in the cluster, helm rollback myapp 2 puts you back exactly where revision 2 was. That is the feature people fall in love with the first time an upgrade goes sideways at two in the morning. Helm keeps the last ten revisions by default and quietly prunes older ones, which you can change if you need a longer trail. And uninstalling a release removes the objects Helm created and, unless you ask it to keep the history, forgets the release as well.

Your secrets end up in the release

The release Secret stores the fully rendered manifests, so any secret values you passed in are sitting inside it. Treat release storage as sensitive, keep plaintext secrets out of values files you commit to version control, and reach for a proper secrets tool when it matters. It is also why a release has a rough size ceiling of about one megabyte, which very large charts can bump into.

04

Where charts come from

You do not have to write charts to use Helm, and most people start out installing ones other people wrote. Charts live in repositories, which you add once with helm repo add, and from then on you can install anything in one by name. A repository is really just a web location holding an index file that lists the available charts and a packaged file for each one, a compressed .tgz. The big public directory for finding them is Artifact Hub, where you can search across thousands of charts from many projects.

Increasingly, charts are also kept in OCI registries, which are the same kind of registry that stores your container images. That means a chart and the images it installs can live side by side in one place, which is tidy and makes access control simpler. Helm 4 treats OCI registries as a first-class home for charts, so this is becoming the common way teams share their own.

05

Where to go next

The way to make this stick is to install a real chart and poke at it. Install something small, then run helm get manifest to see exactly what Helm put into the cluster, change a value and run helm upgrade, then helm rollback and watch it revert. Ten minutes of that teaches more than any amount of reading, and everything in this primer will fall into place while you do it.

The official documentation at helm.sh is the authoritative reference for every command and template function, and Artifact Hub is where you will find charts worth trying. If you want the layer underneath, how the cluster you are deploying into actually works, that is the companion primer on Kubernetes. And once you would rather stop running installs and upgrades by hand at all, the primer on how GitOps works covers how a repository becomes the only route into your clusters. And for the question underneath all of it, whether the thing you are packaging was the right thing to build, the aside on designing systems that solve the right problem steps outside the stack entirely.