Helm 3 is a single binary that runs on your machine. There is nothing to install into the cluster. It authenticates with your existing kubeconfig and talks to the Kubernetes API as you.
That means whatever Helm can do, you could already do with kubectl. If your kubeconfig cannot create Deployments in a namespace, neither can Helm.
brew install helmcurl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bashOn Debian or Ubuntu you can use the apt repository instead, which gets you upgrades through the package manager. Piping a script to a shell is convenient but worth reading first on a machine you care about.
choco install kubernetes-helmwinget install Helm.Helm and scoop install helm both work too.
helm versionversion.BuildInfo{Version:"v3.16.3", GitCommit:"cfd07493f...", GoVersion:"go1.22.7"}
Confirm it is v3.x. Any guide telling you to run helm init or mentioning Tiller is describing Helm 2, which is end-of-life. Helm 3 has no cluster-side component and no initialisation step.
Helm uses the same kubeconfig and current context as kubectl, so check where you are pointed before installing anything:
kubectl config current-contextA repository is an index of charts served over HTTP. Add one, then refresh the local cache:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo updatehelm repo list
helm search repo nginxNAME CHART VERSION APP VERSION DESCRIPTION
bitnami/nginx 18.2.1 1.27.2 NGINX Open Source is a web server...
bitnami/nginx-ingress-controller 11.6.7 1.11.3 NGINX Ingress Controller...
helm repo update is the step people forget. Without it you install whatever version was current when you added the repository, which can be months old. helm search repo only ever searches your local cache.
To search everything published publicly instead, use helm search hub.
Many projects now publish to OCI registries rather than classic repositories, in which case there is no repo add at all. You reference the chart directly:
helm install my-app oci://registry-1.docker.io/bitnamicharts/nginxhelm show values bitnami/nginx > nginx-values.yamlDo this every time. Charts commonly expose a hundred or more settings, and the defaults are tuned for a demo, usually one replica, no resource limits, and persistence switched off. Installing straight from defaults is how you end up with an unlimited pod that gets evicted under memory pressure.
Also worth a look before installing something unfamiliar:
helm show chart bitnami/nginx # metadata and dependencies
helm show readme bitnami/nginx # the chart's own documentation
helm template my-nginx bitnami/nginx -f nginx-values.yaml | less # rendered manifestshelm install my-nginx bitnami/nginx -f nginx-values.yamlmy-nginx is the release name. Your name for this installation, not the chart's name. It must be unique within the namespace.
Namespaces are worth being explicit about, because Helm does not create them for you by default:
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespaceFor anything automated, prefer the idempotent form. It installs if the release is absent and upgrades if it exists, so the same command works on first run and every run after:
helm upgrade --install my-nginx bitnami/nginx -f nginx-values.yamlhelm listNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-nginx default 1 2026-09-12 10:04:11 deployed nginx-18.2.1 1.27.2
helm list is namespace-scoped. A release you cannot find is usually in another namespace:
helm list -Ahelm status my-nginx # status plus the chart's post-install notes
helm get values my-nginx # the values you supplied
helm get manifest my-nginx # exactly what was applied
helm history my-nginx # every revisionhelm get values my-nginx --all shows the merged result of your overrides and the chart defaults, which answers "why is this setting not what I expected" faster than anything else.
helm upgrade my-nginx bitnami/nginx -f nginx-values.yaml --atomic --timeout 5m--atomic makes the upgrade all-or-nothing, if the new resources do not become ready within the timeout, Helm reverts the release rather than leaving it half-applied. Use it every time.
helm history my-nginx
helm rollback my-nginx 1helm uninstall my-nginxThis removes the resources Helm created and the release history. Two things it does not remove:
crds/ directory, because removing them would delete every custom resource of that type in the cluster.# Check for leftovers
kubectl get pvc -n <namespace>Use --keep-history if you want the release history retained for reference after removal.
| Task | Command |
|---|---|
| Check the version | helm version |
| Add a repository | helm repo add <name> <url> |
| Refresh the cache | helm repo update |
| Search locally | helm search repo <term> |
| Search publicly | helm search hub <term> |
| Read a chart's settings | helm show values <chart> |
| Render without installing | helm template <release> <chart> |
| Install | helm install <release> <chart> -f values.yaml |
| Install or upgrade | helm upgrade --install <release> <chart> |
| All releases | helm list -A |
| Applied values | helm get values <release> --all |
| History | helm history <release> |
| Roll back | helm rollback <release> <revision> |
| Uninstall | helm uninstall <release> |
helm init and Tiller are Helm 2. If a guide mentions them, it is out of date.helm repo update or you will install a stale chart version from your local cache.helm show values first. Chart defaults are demo defaults.helm upgrade --install is the idempotent form to use in automation.helm list is namespace-scoped: use -A when a release seems to have vanished.<MCQExercise exercise={{ id: 'k8s-helm-install-missing-release', title: 'A release that disappeared', description: 'A colleague installed a release yesterday, but helm list on your machine shows nothing. Both of you use the same cluster and the same kubeconfig. What is the most likely explanation?', options: [ { id: 'a', text: 'The release is in a different namespace, helm list is namespace-scoped, so helm list -A will find it' }, { id: 'b', text: 'Release state is stored locally, so it is only visible on the machine that installed it' }, { id: 'c', text: 'The release expired because Helm history is retained for 24 hours' }, { id: 'd', text: 'helm repo update must be run before helm list can see existing releases' }, ], correctId: 'a', explanation: 'Helm 3 stores release state as Secrets in the release's own namespace, so helm list only reports the current namespace unless you pass -A or -n. Anyone with read access to that namespace sees the same releases; there is nothing machine-specific about it.', wrongExplanations: { b: 'That was closer to true in some Helm 2 configurations. Helm 3 keeps state in the cluster precisely so it is shared and survives losing a laptop.', c: 'Release history has no expiry. Revisions are kept until you uninstall the release or exceed --history-max.', d: 'helm repo update refreshes the local index of available charts. It has nothing to do with listing installed releases.', }, }} />
<MCQExercise exercise={{ id: 'k8s-helm-install-uninstall-pvc', title: 'Storage costs after an uninstall', description: 'You run helm uninstall on a database release. Weeks later the cloud bill still shows the volume. Why?', options: [ { id: 'a', text: 'Most charts deliberately leave PersistentVolumeClaims behind so data survives an uninstall; they must be deleted separately' }, { id: 'b', text: 'helm uninstall only removes the release record and leaves every resource running' }, { id: 'c', text: 'The volume is retained for 90 days before automatic deletion' }, { id: 'd', text: 'PVCs can only be removed by deleting the entire namespace' }, ], correctId: 'a', explanation: 'Removing a workload should not silently destroy its data, so charts generally exclude PVCs from deletion. The same reasoning that keeps StatefulSet claims after the StatefulSet is gone. The claim, and the cloud disk behind it, persist until someone deletes it. Check with kubectl get pvc after any uninstall that involved storage.', wrongExplanations: { b: 'Uninstall does delete the Deployments, Services and other resources Helm created. PVCs and CRDs are the deliberate exceptions.', c: 'Neither Helm nor Kubernetes has any timed deletion for volumes. Nothing will clean it up on its own.', d: 'kubectl delete pvc <name> removes a single claim. Deleting the namespace works but is a much broader action than needed.', }, }} />
No. helm init and Tiller belong to Helm 2, which reached end of life. Tiller was a cluster-side service that usually held broad permissions and was a recognised security risk. Helm 3 removed it: the client talks directly to the Kubernetes API using your credentials, and release state is stored as Secrets in each release's namespace. Any tutorial mentioning helm init is out of date and should be treated with suspicion generally.
helm install fails if a release with that name already exists. helm upgrade --install installs it when absent and upgrades it when present, making the command idempotent. That is what you want in CI or any script that may run more than once, since it removes the need to check state first. Adding --atomic makes it safer still by rolling back automatically if the release does not become ready.
helm list only shows releases in the current namespace, because Helm 3 stores release state as Secrets in the namespace where the release lives. Run helm list -A to search every namespace, or helm list -n <namespace> to target one. If it is still missing, confirm you are on the expected cluster with kubectl config current-context. Pointing at the wrong context is the other common cause.
Not quite, and the exceptions are deliberate. PersistentVolumeClaims are usually left in place so that uninstalling an application does not destroy its data, which means the underlying cloud volumes keep incurring cost until you delete the claims yourself. CRDs installed from a chart's crds/ directory are also left alone, because deleting a CRD would delete every custom resource of that type across the cluster. After uninstalling anything stateful, check kubectl get pvc in that namespace.
helm template <release-name> <chart> -f values.yaml renders the chart locally and prints the full set of manifests without contacting the cluster. This is the best way to review a third-party chart before trusting it, and to debug your own templates. helm install --dry-run --debug does something similar but validates against the live API server, which additionally catches schema problems and missing CRDs. Pair either with helm show values so you know which settings are available to change.
Run zero-downtime rolling updates in Kubernetes. Tune maxSurge and maxUnavailable, use readiness probes and graceful shutdown, and fix stalled rollouts.
Roll back a bad release with kubectl rollout undo. Learn revision history, change-cause annotations, revisionHistoryLimit and what a rollback cannot fix.
Learn how a Kubernetes Service finds pods with selectors, how port, targetPort and nodePort differ, how service DNS works, and which of the four types to pick.