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 kubectl installed and a kubeconfig that can reach the cluster.
  • A non-root user with sudo privileges 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"}

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.

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.

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 demo shows the release status and notes again.
  • helm get manifest my-podinfo -n demo prints the exact Kubernetes objects Helm applied.
  • helm list -A lists 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, including version (the chart version) and appVersion (the application version).
  • values.yaml: default values that templates read through .Values.
  • templates/: Go templates for the Kubernetes objects, plus _helpers.tpl for shared naming and label helpers and NOTES.txt for post-install instructions.
  • templates/tests/: test pods that helm test runs.
  • 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. Run kubectl get nodes; if that fails too, fix ~/.kube/config or set KUBECONFIG to 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. Use helm upgrade --install to create or update it idempotently, or pick another name.
  • another operation (install/upgrade/rollback) is in progress: a previous command was interrupted. Check helm history, then roll back to the last deployed revision with helm rollback.
  • Values seem to be ignored: compare your keys with helm show values for 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.