Helm is the package manager for Kubernetes. It bundles the manifests an application needs (Deployments, Services, ConfigMaps and so on) into a versioned, parameterized package called a chart, and tracks every installation of a chart as a release that you can upgrade or roll back with one command. In this tutorial you will install the Helm CLI on an Ubuntu 24.04 machine, deploy a public chart, customize it with a values file, and then create, test and roll back a chart of your own.
Prerequisites
To follow this tutorial you need:
- A running Kubernetes cluster (1.30 or newer). It can be a managed cluster or a self-managed k3s or kubeadm cluster on a CubePath VPS.
- A workstation or server running Ubuntu 24.04 LTS with
kubectlinstalled and a kubeconfig that can reach the cluster. - A non-root user with
sudoprivileges on that machine.
Confirm that kubectl talks to the right cluster before you start:
kubectl cluster-info
Kubernetes control plane is running at https://your_server_ip:6443
CoreDNS is running at https://your_server_ip:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Helm uses the same kubeconfig and current context as kubectl, so whatever cluster kubectl points to is where Helm will install releases.
Step 1 - Installing the Helm CLI
Helm is a single static binary. The most predictable way to install it is to download a release tarball from get.helm.sh, verify its checksum and place the binary in /usr/local/bin.
Check the latest version on the Helm releases page and set it in a shell variable. The example uses v4.0.0; replace it with the current release. Use arm64 instead of amd64 on ARM servers.
HELM_VERSION=v4.0.0
ARCH=amd64
Download the tarball and its checksum file:
cd /tmp
curl -fsSLO "https://get.helm.sh/helm-${HELM_VERSION}-linux-${ARCH}.tar.gz"
curl -fsSLO "https://get.helm.sh/helm-${HELM_VERSION}-linux-${ARCH}.tar.gz.sha256sum"
Verify the download:
sha256sum -c "helm-${HELM_VERSION}-linux-${ARCH}.tar.gz.sha256sum"
helm-v4.0.0-linux-amd64.tar.gz: OK
Extract the archive and install the binary:
tar -xzf "helm-${HELM_VERSION}-linux-${ARCH}.tar.gz"
sudo install -m 0755 "linux-${ARCH}/helm" /usr/local/bin/helm
Check that Helm runs:
helm version
version.BuildInfo{Version:"v4.0.0", GitCommit:"...", GitTreeState:"clean", GoVersion:"go1.25.x"}
NoteEverything in this tutorial also works with Helm 3.x. If your team still standardizes on Helm 3, install the latest 3.x release the same way.
Optionally, enable Bash completion so you can tab-complete subcommands, release names and chart names:
helm completion bash | sudo tee /etc/bash_completion.d/helm > /dev/null
Open a new shell for completion to take effect.
Step 2 - Adding a chart repository
Charts are distributed through HTTP chart repositories or OCI registries. In this tutorial you will use podinfo, a small, well-maintained demo web application that is ideal for learning Helm.
Add its repository and refresh the local index:
helm repo add podinfo https://stefanprodan.github.io/podinfo
helm repo update
"podinfo" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "podinfo" chart repository
Update Complete. ⎈Happy Helming!⎈
List the configured repositories and search for the chart:
helm repo list
helm search repo podinfo
NAME URL
podinfo https://stefanprodan.github.io/podinfo
NAME CHART VERSION APP VERSION DESCRIPTION
podinfo/podinfo 6.x.x 6.x.x Podinfo Helm chart for Kubernetes
Add --versions to helm search repo to see every published version of a chart. helm repo update only refreshes your local cache, so run it before installing or upgrading when you want the newest chart versions.
TipMany projects now publish charts to OCI registries instead of HTTP repositories. Those charts need no
helm repo add; you reference them directly, for exampleoci://ghcr.io/stefanprodan/charts/podinfo.
Step 3 - Installing a chart and customizing values
Every chart ships a values.yaml file with its defaults. Before installing, look at what you can change:
helm show values podinfo/podinfo | less
Create a dedicated namespace and install the chart as a release called my-podinfo. Pin the chart version with --version so the install is reproducible; use the version that helm search repo showed.
helm install my-podinfo podinfo/podinfo \
--namespace demo --create-namespace \
--version chart_version
Replace chart_version with the actual version number. Helm prints the release status and the chart's usage notes:
NAME: my-podinfo
LAST DEPLOYED: ...
NAMESPACE: demo
STATUS: deployed
REVISION: 1
NOTES:
...
Check the release and the pods it created:
helm list -n demo
kubectl get pods -n demo
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-podinfo demo 1 ... deployed podinfo-6.x.x 6.x.x
NAME READY STATUS RESTARTS AGE
my-podinfo-5c9d8b7f6d-8kq2x 1/1 Running 0 40s
Overriding values with a file
You can override individual values with --set key=value, but a values file is easier to review and keep in Git. Create one:
nano podinfo-values.yaml
replicaCount: 2
ui:
message: "Deployed with Helm on CubePath"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 128Mi
Apply it with helm upgrade. Helm merges the file on top of the chart defaults, so it only needs the keys you want to change.
helm upgrade my-podinfo podinfo/podinfo \
-n demo --version chart_version \
-f podinfo-values.yaml
Confirm the values Helm stored for the release and that two pods are now running:
helm get values my-podinfo -n demo
kubectl get pods -n demo
USER-SUPPLIED VALUES:
replicaCount: 2
resources:
...
ui:
message: Deployed with Helm on CubePath
To see the application respond, forward the service port to your machine. The podinfo service listens on port 9898:
kubectl port-forward -n demo svc/my-podinfo 8080:9898
In a second terminal, query it:
curl -s http://localhost:8080 | grep message
"message": "Deployed with Helm on CubePath",
Press CTRL+C in the first terminal to stop the port forward.
WarningValues are stored in the release record inside the cluster and often end up in Git. Do not pass database passwords or API keys with
--setor commit them in values files. Most charts accept anexistingSecretstyle value that points to a Kubernetes Secret you create separately.
Step 4 - Upgrading, inspecting and rolling back releases
Each helm install or helm upgrade creates a new revision of the release. Look at the history of my-podinfo:
helm history my-podinfo -n demo
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 ... superseded podinfo-6.x.x 6.x.x Install complete
2 ... deployed podinfo-6.x.x 6.x.x Upgrade complete
Before an upgrade, preview the rendered manifests without changing the cluster:
helm upgrade my-podinfo podinfo/podinfo -n demo --version chart_version \
-f podinfo-values.yaml --set replicaCount=3 --dry-run
If a real upgrade goes wrong, roll back to a known good revision. Here you return to revision 1, the original install with a single replica:
helm rollback my-podinfo 1 -n demo
Rollback was a success! Happy Helming!
The rollback itself is recorded as a new revision (3), so the history stays linear and auditable. Verify that the deployment is back to one replica:
kubectl get deployment my-podinfo -n demo
NAME READY UP-TO-DATE AVAILABLE AGE
my-podinfo 1/1 1 1 6m
Other useful inspection commands:
helm status my-podinfo -n demoshows the release status and notes again.helm get manifest my-podinfo -n demoprints the exact Kubernetes objects Helm applied.helm list -Alists releases in all namespaces.
Step 5 - Creating your own chart
helm create scaffolds a working chart that deploys NGINX, with a Deployment, Service, optional Ingress, HorizontalPodAutoscaler and a connection test. It is a good starting point for packaging your own application.
helm create my-web-app
The generated layout:
Chart.yaml: chart metadata, includingversion(the chart version) andappVersion(the application version).values.yaml: default values that templates read through.Values.templates/: Go templates for the Kubernetes objects, plus_helpers.tplfor shared naming and label helpers andNOTES.txtfor post-install instructions.templates/tests/: test pods thathelm testruns.charts/: dependency charts, if any.
Open values.yaml and set the image your application uses. For this walkthrough, keep NGINX but pin a tag instead of relying on appVersion:
nano my-web-app/values.yaml
replicaCount: 2
image:
repository: nginx
pullPolicy: IfNotPresent
tag: "1.27"
Leave the rest of the file as generated. Now validate the chart. helm lint checks the chart structure and templates for errors:
helm lint ./my-web-app
==> Linting ./my-web-app
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
Render the templates locally to see exactly what will be sent to the cluster:
helm template my-web-app ./my-web-app | less
Install the chart from the local directory:
helm install my-web-app ./my-web-app -n demo
Run the chart's built-in test, which starts a pod that connects to the service:
helm test my-web-app -n demo
NAME: my-web-app
...
TEST SUITE: my-web-app-test-connection
Last Started: ...
Last Completed: ...
Phase: Succeeded
When the chart is ready to share, bump version in Chart.yaml and package it:
helm package ./my-web-app
Successfully packaged chart and saved it to: /home/your_user/my-web-app-0.1.0.tgz
You can publish the .tgz to any OCI registry that supports Helm charts (for example Harbor or GitHub Container Registry) with helm push my-web-app-0.1.0.tgz oci://your_registry/charts after logging in with helm registry login.
Step 6 - Cleaning up
Uninstall both releases and delete the namespace:
helm uninstall my-podinfo my-web-app -n demo
kubectl delete namespace demo
release "my-podinfo" uninstalled
release "my-web-app" uninstalled
namespace "demo" deleted
helm uninstall removes every object the release created, except PersistentVolumeClaims created by StatefulSets and resources annotated with helm.sh/resource-policy: keep.
Troubleshooting
Error: INSTALLATION FAILED: Kubernetes cluster unreachable: Helm cannot read a working kubeconfig. Runkubectl get nodes; if that fails too, fix~/.kube/configor setKUBECONFIGto the right file.Error: INSTALLATION FAILED: cannot re-use a name that is still in use: a release with that name already exists in the namespace. Usehelm upgrade --installto create or update it idempotently, or pick another name.another operation (install/upgrade/rollback) is in progress: a previous command was interrupted. Checkhelm history, then roll back to the lastdeployedrevision withhelm rollback.- Values seem to be ignored: compare your keys with
helm show valuesfor that chart version. A typo in a key is silently ignored by most charts.
Conclusion
You installed the Helm CLI with a verified binary, deployed and customized a chart from a repository, used revisions to upgrade and roll back a release, and scaffolded, tested and packaged a chart of your own. From here, consider storing your values files in Git and deploying them with a GitOps tool such as Argo CD or Flux, adding chart dependencies in Chart.yaml for databases or caches, and restricting what each release can do in the cluster with Kubernetes RBAC.
