Helm Tutorial: Package and Deploy Kubernetes Apps
Helm is the package manager for Kubernetes. This guide takes you from install to a real templated deployment you can upgrade and roll back.
What is Helm?
Helm is the package manager for Kubernetes. Instead of maintaining a sprawl of raw YAML manifests and applying them by hand, you describe an application once as a chart, parameterize the parts that change between environments, and let Helm install, upgrade, and roll it back as a single unit. If you have ever copied a Deployment manifest, changed the image tag and replica count by hand for staging, and hoped you did not miss a field, Helm replaces that workflow.
A chart is a versioned, shareable artifact. The Kubernetes community publishes thousands of them - PostgreSQL, Prometheus, ingress-nginx, cert-manager - so deploying a well-tested stack often takes one command. Helm is a graduated CNCF project and the standard tool for packaging Kubernetes applications. The current major line is Helm 4, which added Kubernetes server-side apply, a redesigned plugin system, and better resource-readiness checks, while keeping the same core workflow you will learn here.
How Helm works (key concepts)
A handful of concepts cover almost everything you do day to day:
- Chart - the package. A directory (or a packaged
.tgz) containing aChart.yaml(name, version, app version), atemplates/directory of Kubernetes manifests written as Go templates, and avalues.yamlof default configuration. - Release - one installed instance of a chart, identified by a name you choose. Install the same chart three times and you get three independent releases, each with its own values and revision history.
- Values - the configuration you inject at install or upgrade time. Defaults live in the chart's
values.yaml; you override them with--values myvalues.yamlor--set key=value. Templates read these through the.Valuesobject. - Repository - an HTTP server or OCI registry that hosts packaged charts. You add a repo, then reference charts as
repo/chart. OCI registries (the same registries that hold container images) are now the preferred distribution method. - Revision - each install or upgrade increments a revision number. Helm stores release state as Secrets in the target namespace, which is what makes
helm rollbackandhelm historypossible.
Critically, Helm 3 and later run entirely client-side. There is no in-cluster Tiller server to install or secure. Helm authenticates with your kubeconfig, renders templates locally into final manifests, and sends them to the Kubernetes API. Your RBAC permissions are exactly the permissions Helm has - nothing more.
Installing Helm
The fastest path on macOS and Linux is a package manager. Homebrew tracks releases closely:
# macOS or Linux with Homebrew
brew install helm
Most Linux distributions can also install Helm from a package repository (apt, dnf, snap). The official Helm install docs list the current repository setup for each one.
If you prefer not to use a package manager, the official installer script fetches and installs the latest binary:
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-4
chmod 700 get_helm.sh
./get_helm.sh
Read the script before running it - it is a normal shell installer with all the usual caveats. Confirm the install and that Helm can see your cluster:
helm version
kubectl config current-context # Helm uses this same kubeconfig context
Hands-on: deploy and upgrade a real chart
Let's deploy something real, override its configuration, then upgrade and roll back. We'll use the open-source podinfo demo chart, published to an OCI registry, which needs no helm repo add.
First, do a dry run to render the manifests Helm would apply, without touching the cluster:
helm install web oci://ghcr.io/stefanprodan/charts/podinfo \
--namespace web --create-namespace \
--set service.type=ClusterIP \
--dry-run
When the rendered output looks right, drop --dry-run to actually install. Helm creates the namespace and tracks the release as web:
helm install web oci://ghcr.io/stefanprodan/charts/podinfo \
--namespace web --create-namespace \
--set service.type=ClusterIP
helm list -n web
helm status web -n web
For anything beyond a quick toggle, put overrides in a file so the configuration is reviewable and version-controlled:
# web-values.yaml
replicaCount: 3
service:
type: ClusterIP
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
Apply it with helm upgrade. Adding --install makes the command create the release if it does not exist yet, which is the idempotent pattern you want in CI:
helm upgrade --install web oci://ghcr.io/stefanprodan/charts/podinfo \
--namespace web \
--values web-values.yaml
Each upgrade is a new revision. Inspect the history and, if a change misbehaves, roll back to the previous good revision in one command:
helm history web -n web
helm rollback web 1 -n web # revert to revision 1
To build your own chart, scaffold one and explore the generated structure - Chart.yaml, values.yaml, and the templates/ directory:
helm create myapp
helm lint myapp
helm template myapp # render templates locally to inspect output
When you are done with a release, remove it and all the resources it created:
helm uninstall web -n web
When to use Helm
Reach for Helm when you deploy the same application across multiple environments, when you depend on third-party components that are published as charts, or when you want install, upgrade, and rollback to be one auditable command rather than a folder of manifests applied in the right order by hand. The revision history and one-command rollbacks are reason enough to use it for anything running in production.
Helm is less compelling for a single static manifest you apply once and never parameterize - plain kubectl apply is simpler there. Templating can also get unwieldy for very large config surfaces, which is where tools like Kustomize, or Helm's --post-renderer option, come in. A common, practical pattern is to use Helm for packaging and distribution and Kustomize for final, environment-specific patches. For most teams running real services on Kubernetes, though, Helm is the default, and its install/upgrade/rollback loop is useful from the first day.
Frequently asked questions
What is Helm?
Helm is the package manager for Kubernetes. It bundles a set of Kubernetes manifests into a versioned, parameterized unit called a chart, then installs, upgrades, and rolls back that chart as a single named release. It removes the need to hand-edit and kubectl apply dozens of YAML files.
What is the difference between a chart and a release?
A chart is the package: templates, default values, and metadata. A release is one installed instance of a chart in a cluster, tracked by name. You can install the same chart many times under different release names, each with its own values and revision history.
Do I still need Tiller like in Helm 2?
No. Tiller, the in-cluster server component, was removed in Helm 3. Helm now runs entirely client-side and talks to the Kubernetes API using your kubeconfig. Release state is stored as Secrets in the target namespace, so RBAC governs what Helm can do.
How do I undo a bad Helm upgrade?
Run helm rollback RELEASE REVISION to revert to an earlier revision, or helm rollback RELEASE with no number to go back one. Use helm history RELEASE to list revisions and helm status RELEASE to inspect the current state before rolling back.
Do real production work, free. Live cloud workspaces in your browser - every project you complete lands on your portfolio.
Get started - free