Helm for Kubernetes

By Vishvesh Patel · DevOps EngineerUpdated September 12, 2026

What Helm is and when it earns its complexity. Charts, releases, repositories, how upgrades and rollbacks work, and how it compares to Kustomize.

Helm is the package manager for Kubernetes. It bundles a set of manifests into a versioned, parameterised chart, installs it as a named release, and tracks enough history to upgrade and roll back.

Its two jobs are genuinely different, and it is worth being clear which one you need: consuming third-party software, and packaging your own.

Three terms

TermMeaning
ChartThe package: templates plus default values plus metadata
ReleaseOne installed instance of a chart, with a name and a version history
RepositoryA place charts are published and fetched from

The chart/release distinction matters: one chart can be installed many times in the same cluster under different names, each with its own values and its own history.

The case for Helm

Installing other people's software. This is where Helm is uncontested. Prometheus, ingress-nginx, cert-manager, Grafana and almost everything else in the ecosystem ships as a chart. Installing Prometheus by hand means dozens of interdependent manifests with CRDs, RBAC and service discovery configuration:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace

Packaging your own applications. Here it is a real trade-off. You gain one chart serving every environment through values files; you pay in Go templating, which is genuinely hard to read once it grows conditionals and helper functions.

Helm 3 removed the thing people disliked

Helm 2 ran a cluster-side component called Tiller that needed broad permissions and became a well-known security problem. Helm 3 is client-side only. It talks to the Kubernetes API with your own kubeconfig credentials, so it can do exactly what you can do, no more.

Release state is stored as Secrets in the release's namespace, which is why helm list is namespace-scoped and why helm list -A exists.

The workflow

# Find and inspect
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo nginx
helm show values bitnami/nginx > values.yaml

# Install
helm install my-nginx bitnami/nginx -f values.yaml

# Observe
helm list
helm status my-nginx

# Change
helm upgrade my-nginx bitnami/nginx -f values.yaml --atomic

# Undo
helm rollback my-nginx 1

# Remove
helm uninstall my-nginx
helm list
NAME       NAMESPACE  REVISION  STATUS    CHART         APP VERSION
my-nginx   default    3         deployed  nginx-18.2.1  1.27.2

Two habits worth forming early:

helm show values <chart> > values.yaml before installing anything. Charts have dozens or hundreds of settings and the defaults are rarely what you want in production. Resource limits, replica counts and persistence settings especially.

--atomic on every upgrade. Without it a failed upgrade leaves the release half-applied. With it, Helm rolls back automatically. Pair it with --timeout so it does not wait forever.

What Helm actually does at upgrade time

This surprises people. Helm does not diff against the live cluster by default. It computes a three-way merge between the old release manifest, the new one, and the current cluster state, then applies the result.

Two practical consequences:

Manual kubectl edit changes may be preserved or clobbered depending on the field, which is why mixing Helm with hand edits produces confusing results. Pick one.

Removing a resource from a chart does delete it on upgrade, because Helm tracks what it created. That is usually what you want, and occasionally alarming.

Preview before applying:

helm diff upgrade my-nginx bitnami/nginx -f values.yaml   # requires helm-diff plugin
helm upgrade my-nginx bitnami/nginx -f values.yaml --dry-run

The helm-diff plugin is the single most useful thing you can add to Helm. Install it.

Where Helm is weak

CRDs. Custom Resource Definitions in a chart's crds/ directory are installed once and never upgraded or deleted by Helm. Upgrading a chart that changed its CRDs requires applying them yourself. This causes real, confusing failures.

Templating readability. A chart with nested conditionals and _helpers.tpl indirection is hard to reason about. helm template to see the rendered output is often the only way to be sure.

Rollback is not a time machine. helm rollback reverts Kubernetes objects. It does not undo a database migration, and it will not restore data deleted by a PVC that the upgrade removed.

Helm or Kustomize?

HelmKustomize
ApproachTemplating with variablesPatching plain YAML
ReadabilityHarder as charts growBase manifests stay valid YAML
DistributionRepositories, versioned packagesNo packaging story
Release trackingBuilt in, with rollbackNone (relies on Git)
Built into kubectlNoYes, kubectl apply -k
EcosystemEverything ships as a chartMostly internal use

The pragmatic answer most teams land on: Helm for third-party software, Kustomize for your own manifests. You get the ecosystem without writing Go templates for your own services, and your manifests stay readable YAML. ArgoCD and Flux both support either, and both support Kustomize overlays on top of a rendered Helm chart, which is a genuinely good pattern.

Explore further in this section

Sub-topicWhat you will learn
Installing HelmInstall Helm and use the repo and release commands
Understanding ChartsChart structure, templates and built-in objects
Values and UpgradesOverride configuration, upgrade and roll back safely

Quick reference

TaskCommand
Add a repositoryhelm repo add <name> <url>
Searchhelm search repo <term>
Read a chart's settingshelm show values <chart>
Installhelm install <release> <chart> -f values.yaml
Install or upgradehelm upgrade --install <release> <chart>
Releases everywherehelm list -A
Historyhelm history <release>
Roll backhelm rollback <release> <revision>
Render without installinghelm template <release> <chart>
Uninstallhelm uninstall <release>

Key takeaways

  • Chart is the package, release is the installed instance. One chart, many releases.
  • Helm 3 is client-side: no Tiller, and it acts with your own kubeconfig permissions.
  • Run helm show values before installing anything. Chart defaults are rarely production-appropriate.
  • Always upgrade with --atomic so a failure rolls back instead of leaving a half-applied release.
  • CRDs in crds/ are never upgraded by Helm. Apply CRD changes yourself.
  • helm rollback reverts Kubernetes objects only: not migrations, not deleted data.
  • Helm for third-party software, Kustomize for your own is a defensible default.

Check your understanding

Exercise

A chart upgrade that half worked

You upgrade an operator chart to a version whose custom resources have new fields. The controller starts, but applying a resource with the new fields fails validation. What is the most likely cause?

Exercise

A stuck release

A helm upgrade fails partway through. The release shows status failed and some resources are on the new version while others are on the old. Which flag would have prevented this?


Frequently Asked Questions

What is Helm and do I actually need it?

Helm is a package manager for Kubernetes that bundles manifests into versioned, parameterised charts and tracks each installation as a release with upgrade and rollback history. You almost certainly need it for consuming third-party software, because Prometheus, ingress-nginx, cert-manager and most of the ecosystem ship as charts and installing them by hand means managing dozens of interdependent manifests. Whether you package your own applications with it is a genuine choice. Many teams use Helm for external software and Kustomize for their own services, keeping their manifests as readable YAML.

What is the difference between a chart and a release?

A chart is the package: templates, default values and metadata, versioned and distributable. A release is one installed instance of that chart in a cluster, with its own name, its own values and its own revision history. The same chart can be installed many times under different names, which is how you run several instances of the same application side by side. helm list shows releases, not charts.

What is the difference between Helm and Kustomize?

Helm uses Go templating with variables substituted at render time, and adds packaging, distribution and release tracking with rollback. Kustomize patches plain YAML through overlays, so base manifests remain valid Kubernetes YAML you can apply directly, and it is built into kubectl via apply -k. Helm is stronger for distributing software to other people and for managing installed releases; Kustomize is easier to read and reason about for your own manifests. Using both, Helm for third-party charts, Kustomize for internal services, is common and works well.

Why doesn't helm upgrade update my CRDs?

By design. CRDs placed in a chart's crds/ directory are installed once on first install and never upgraded or deleted afterwards, because an incorrect CRD change can invalidate or destroy every custom resource of that type in the cluster. Helm takes the view that this is too destructive to automate. When a chart upgrade includes CRD changes, apply them yourself with kubectl apply -f before running the upgrade. The chart's release notes normally say so, and skipping it produces validation errors that look unrelated.

How do I roll back a Helm release?

helm history <release> lists the revisions, then helm rollback <release> <revision> reverts to one. Helm stores each revision's manifest as a Secret in the release namespace, so rollback simply reapplies an earlier state. Two caveats: it only reverts Kubernetes objects, so a database migration run by the bad release still needs handling separately, and data lost through a deleted PersistentVolumeClaim is not coming back. Upgrading with --atomic avoids most rollbacks by reverting automatically on failure.

Is Helm safe to use in production? What happened to Tiller?

Helm 3 is safe in a way Helm 2 was not. Helm 2 ran a cluster-side component called Tiller that typically held broad cluster-admin permissions and was a well-documented privilege escalation risk. Helm 3 removed it entirely and runs client-side, authenticating with your own kubeconfig, so it can only do what you are already permitted to do. Release state lives in Secrets in the release's namespace rather than in a privileged service. Any guide that mentions helm init or Tiller is describing Helm 2 and is out of date.

Continue Learning

Explore Related Topics

Try the Tool

Practise this in the browser

Kubernetes Playground

Practise kubectl against a simulated cluster in your browser.

When it breaks

Related Resources