Getting Started with Helm Package Manager on Kubernetes

Written by: Bagus Facsi Aginsa
Published at: 17 Jun 2026


If you have spent any time with Kubernetes, you already know the feeling. You want to deploy something simple, say a database or an Nginx server, and suddenly you are juggling a Deployment, a Service, a ConfigMap, maybe a Secret, and an Ingress. That is five YAML files for one app. Now multiply that by every environment you run (dev, staging, production) where only a handful of values actually change, and the copy-paste mess starts to feel unmanageable.

Helm solves exactly this. It is the package manager for Kubernetes, often described as “apt or yum, but for clusters.” Instead of hand-writing and tracking dozens of manifests, you install a single packaged unit called a chart, override only the values you care about, and let Helm handle the rest. Upgrades, rollbacks, and clean uninstalls become single commands.

This tutorial is for developers, sysadmins, and DevOps engineers who already have a working Kubernetes cluster and want to stop managing raw YAML by hand. By the end you will have installed Helm, deployed a real application from a public chart, customized it with your own values, upgraded and rolled it back, and built a small chart of your own from scratch.

If you do not have a cluster yet, set one up first using my earlier guide on installing single-node Kubernetes. If your LoadBalancer services never get an external IP, my MetalLB guide will help.


Conceptual Overview

Before touching commands, let us get the vocabulary straight. Helm has only a few core concepts, and once they click everything else makes sense.

A chart is a package. It is a folder (or a compressed .tgz file) containing templated Kubernetes manifests plus some metadata. Think of it as a blueprint for an application. A chart for PostgreSQL knows how to create the StatefulSet, Service, and Secret that PostgreSQL needs.

A release is one installed instance of a chart in your cluster. If you install the same Nginx chart three times with three different names, you have three releases. This is important: charts are reusable templates, releases are the running results.

A values file is where customization happens. Charts ship with sensible defaults in a values.yaml file, but you override anything you like (replica count, image tag, resource limits) with your own values without ever editing the chart itself.

A repository is a place where charts are hosted, similar to an apt repository. You add a repo, then install charts from it by name.

One more thing worth knowing: modern Helm (version 3 and later) runs entirely client-side and talks directly to the Kubernetes API. Older tutorials mention a server-side component called Tiller. That was removed years ago. If you read a guide that tells you to helm init or install Tiller, it is outdated, so ignore it.


Prerequisites

To follow along you will need:

  • A working Kubernetes cluster (single-node is fine for learning).
  • kubectl installed and configured, with a valid kubeconfig that can reach your cluster. Test it with kubectl get nodes.
  • An Ubuntu machine (20.04, 22.04, or 24.04) where you run the commands. This can be your control-plane node or a separate workstation.
  • Basic comfort with the Linux command line and a rough idea of what Kubernetes Deployments and Services are. If you want a refresher, see my post on understanding Kubernetes objects.

Confirm your cluster is reachable before going further:

kubectl get nodes

You should see at least one node in Ready state:

NAME       STATUS   ROLES           AGE   VERSION
master-1   Ready    control-plane   12d   v1.30.2

Step 1: Install Helm on Ubuntu

There are several ways to install Helm, but the cleanest on Ubuntu is the official apt repository, because it keeps Helm updated through the normal apt upgrade flow.

First, add the signing key and the repository:

curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list

Then update and install:

sudo apt-get update
sudo apt-get install helm

Verify the installation:

helm version

You should see something like:

version.BuildInfo{Version:"v3.15.2", GitCommit:"...", GitTreeState:"clean", GoVersion:"go1.22.4"}

The key thing to confirm is that the version starts with v3 or higher. That tells you the modern, Tiller-free Helm is running.

Helm reads the same kubeconfig that kubectl uses, so if kubectl already works, Helm will too. No extra wiring needed.


Step 2: Add a Chart Repository

Out of the box, Helm does not know about any charts. You add a repository first. We will use Bitnami, a well-maintained public repo with hundreds of production-ready charts.

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

The repo update command refreshes your local cache of what charts are available, similar to apt update. Run it whenever you want the latest chart versions.

You can search the repo to see what is on offer:

helm search repo bitnami/nginx
NAME            CHART VERSION   APP VERSION   DESCRIPTION
bitnami/nginx   18.1.6          1.27.0        NGINX Open Source is a web server...

Notice the two version columns. Chart version is the version of the Helm package itself, while app version is the version of the software inside it. They move independently, so do not confuse them.


Step 3: Install Your First Release

Let us deploy Nginx. To keep things tidy, we will put it in its own namespace.

kubectl create namespace web
helm install my-web bitnami/nginx --namespace web

Here my-web is the release name you choose, and bitnami/nginx is the chart. Helm prints a summary and some usage notes pulled from the chart itself.

Check what you just created:

helm list --namespace web
NAME     NAMESPACE   REVISION   STATUS     CHART          APP VERSION
my-web   web         1          deployed   nginx-18.1.6   1.27.0

Notice REVISION 1. Every time you change a release, Helm bumps this number, which is what makes rollbacks possible later.

Now look at the actual Kubernetes objects Helm created on your behalf:

kubectl get all --namespace web

You will see a Deployment, a ReplicaSet, a Pod, and a Service, all from a single command. That is the whole point. You did not write a single line of YAML.


Step 4: Customize with Values

Defaults are fine for a demo, but real deployments need tweaking. The first thing to do with any chart is read its available values:

helm show values bitnami/nginx | less

This dumps every configurable option with comments. It is long, but it is the source of truth for what you can change.

Say you want two replicas instead of one, and you want the service exposed as LoadBalancer. Create a small values file with only the keys you care about:

# my-web-values.yaml
replicaCount: 2

service:
  type: LoadBalancer

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 250m
    memory: 256Mi

The trick to understand here is that you only override what you need. Helm merges your file on top of the chart’s defaults, so everything you leave out keeps its default value. You never have to copy the whole values.yaml.

Apply the changes with helm upgrade:

helm upgrade my-web bitnami/nginx --namespace web --values my-web-values.yaml

Check the release again:

helm list --namespace web

The revision is now 2. Confirm the pod count went up:

kubectl get pods --namespace web
NAME                      READY   STATUS    RESTARTS   AGE
my-web-7c9d8f6b4d-2x9pq   1/1     Running   0          40s
my-web-7c9d8f6b4d-kf8mn   1/1     Running   0          40s

A handy habit: a single command can install or upgrade depending on whether the release already exists. This is great for scripts and CI pipelines:

helm upgrade --install my-web bitnami/nginx --namespace web --values my-web-values.yaml

Step 5: Roll Back a Bad Change

Mistakes happen. Suppose your latest upgrade broke something. Helm keeps the history of every revision, so going back is painless.

View the history:

helm history my-web --namespace web
REVISION   UPDATED                    STATUS       CHART          DESCRIPTION
1          Tue Jun 17 09:10:22 2026   superseded   nginx-18.1.6   Install complete
2          Tue Jun 17 09:18:41 2026   deployed     nginx-18.1.6   Upgrade complete

To return to revision 1:

helm rollback my-web 1 --namespace web

Helm creates a new revision (number 3) that matches the state of revision 1. It does not delete history, it moves forward to a known-good state. Verify with helm history again. This safety net is one of the biggest reasons teams adopt Helm in production.


Step 6: Inspect Before You Apply

When you are unsure what a chart will actually create, render it locally without touching the cluster:

helm template my-web bitnami/nginx --values my-web-values.yaml --namespace web

This prints the fully rendered YAML to your terminal so you can review it. A related flag works on real commands too:

helm upgrade my-web bitnami/nginx --namespace web --values my-web-values.yaml --dry-run

The --dry-run flag sends the request to the cluster for validation but does not apply it. Use these two regularly. They turn “I hope this works” into “I know what this does.”


Step 7: Build Your Own Chart

Public charts are great, but eventually you will want to package your own app. Helm scaffolds a complete starter chart for you:

helm create myapp

This creates a myapp/ directory:

myapp/
├── Chart.yaml          # metadata: name, version, description
├── values.yaml         # default configuration values
├── charts/             # subcharts (dependencies) go here
└── templates/          # the templated Kubernetes manifests
    ├── deployment.yaml
    ├── service.yaml
    ├── _helpers.tpl
    └── ...

Open templates/deployment.yaml and you will see Go templating in action. Instead of a hardcoded image, you get something like:

image: ":"

That `` pulls its value straight from values.yaml. To point the chart at your own image, edit values.yaml:

image:
  repository: nginx
  tag: "1.27"

replicaCount: 1

service:
  type: ClusterIP
  port: 80

Before installing, lint the chart to catch obvious errors:

helm lint ./myapp
==> Linting ./myapp
1 chart(s) linted, 0 chart(s) failed

Then install it from the local directory:

helm install demo ./myapp --namespace web

You now have a release running from a chart you wrote. From here you can version it in Chart.yaml, package it with helm package ./myapp, and host it in a repository for your whole team to use.


Common Mistakes and Troubleshooting

“Error: Kubernetes cluster unreachable.” Helm cannot find or reach your cluster. This is almost always a kubeconfig issue, not a Helm issue. Run kubectl get nodes first. If that fails, fix your kubeconfig before blaming Helm.

“cannot re-use a name that is still in use.” You tried to helm install a release name that already exists. Either pick a new name or use helm upgrade instead. This is exactly why helm upgrade --install is so popular.

Pasting the whole values.yaml and editing it. Beginners often copy the entire default values file and tweak a couple of lines. This works, but it means you inherit every default the chart author later changes. Keep your override file minimal and let the chart own its defaults.

Forgetting the namespace. Helm release names are scoped per namespace, and so is helm list. If helm list shows nothing but you are sure you installed something, you are probably looking at the wrong namespace. Add --namespace web or use helm list --all-namespaces.

Confusing chart version and app version. When someone reports “we run Nginx 1.27,” that is the app version. When you pin a deployment for reproducibility, pin the chart version with --version, for example helm install my-web bitnami/nginx --version 18.1.6. Pinning avoids surprise upgrades when the repo publishes a new chart.

A stuck release in pending-upgrade status. This happens when an upgrade is interrupted. Check helm history, then roll back to the last good revision with helm rollback.


Best Practices

A few habits will keep your Helm usage clean and production-safe.

Always pin chart versions in real environments. Use --version so a helm upgrade six months from now does not silently pull a brand new chart with breaking changes. Reproducibility beats convenience.

Keep values files in version control. Your my-web-values.yaml is the real description of how your app is configured. Commit it to Git alongside your code. Never store secrets in plain values files, though. For sensitive data, pull from Kubernetes Secrets or a tool like Sealed Secrets, and see my guide on creating Kubernetes secrets.

Use one values file per environment. A common layout is values-dev.yaml, values-staging.yaml, and values-prod.yaml. You can even stack them: helm upgrade ... -f values-common.yaml -f values-prod.yaml, where the later file wins on conflicts.

Run helm template or --dry-run in CI. Catch rendering errors before they reach the cluster. It is cheap insurance.

Set resource requests and limits. Many charts leave these empty by default, which can let a single app starve a node. Always define them in your values, as shown earlier.

Clean up releases you no longer need. A simple helm uninstall my-web --namespace web removes every object the release created, no orphaned ConfigMaps left behind. This clean teardown is far safer than kubectl delete on individual objects you might forget.


Conclusion

You have covered the full Helm workflow end to end. You installed Helm on Ubuntu, added a repository, deployed a real application as a release, customized it with a minimal values file, upgraded it, rolled it back to a known-good state, inspected changes before applying them, and built a chart of your own from scratch.

That is genuinely most of what you need for day-to-day Kubernetes work. Helm turns a pile of fragile YAML into versioned, repeatable, reversible deployments, which is exactly what you want when real users depend on your cluster.

For next steps, try packaging your chart with helm package and hosting it in a private repository so your team can install it by name. Explore chart dependencies, where one chart pulls in others (a web app that automatically deploys its own Redis, for example). And once you are comfortable, look into GitOps tools like Argo CD or Flux, which deploy your Helm charts automatically whenever you push to Git. Helm is the foundation that all of those build on, and you now have it solid.