Kubernetes gives you raw primitives: Deployments, Services, ConfigMaps, Ingress rules, and more. Wiring all of them together by hand for one app usually means juggling a dozen YAML files, and that count multiplies across every environment you run. Helm exists to collapse that sprawl into a single package you can install, upgrade, and roll back with one command.

The project describes itself as the package manager for Kubernetes, and the comparison holds up in practice.

“Helm is the package manager (analogous to yum and apt) and Charts are packages (analogous to debs and rpms).”

Kubernetes Blog, kubernetes.io

That framing matters more this month than it did a year ago. Helm shipped v4.3.0 on September 9, 2026, and the project cut v3.22.0 the very next day, marking it as the last feature release for the Helm 3 line. Security patches for Helm 3 continue through February 10, 2027, but no new features land there after this release. If you’re starting a new pipeline or refreshing an old one, now is the point to build around Helm 4.

This tutorial installs Helm 4.3.0, connects it to a live Kubernetes cluster running the 1.37 release, deploys a public chart, and then builds a custom chart for a small API service from scratch. By the end you’ll have a working chart in version control, a packaged .tgz file pushed to an OCI registry, and a troubleshooting checklist for when a release goes sideways at 2 a.m.

Before diving into commands, it helps to know what problem Helm actually solves versus plain kubectl apply or a Kustomize overlay. Raw manifests work fine for a handful of static resources, but once you’re managing the same app across three or four environments, with slightly different replica counts, resource limits, and hostnames in each, copy-pasting YAML turns into a source of drift. One environment quietly falls out of sync with the others, and nobody notices until an incident forces a comparison. A chart replaces that copy-paste process with a single template plus one values file per environment, and it tracks every change as a numbered revision you can inspect or reverse.

Prerequisites: What You Need Before You Start

You don’t need a large cluster to follow along. A single-node cluster from kind, minikube, or a managed service like a small GKE or EKS node pool works fine. Here’s what to have ready before Step 1.

ToolMinimum versionWhy you need it
Helm CLI4.3.0 (or 3.22.0 if staying on the 3.x line)Runs every command in this guide
kubectlMatches your cluster’s minor versionTalks to the Kubernetes API server directly for verification
Kubernetes cluster1.34.x through 1.37.xWhere the charts actually get installed
Container registry accessAny OCI-compliant registry (ghcr.io, Docker Hub, ECR)Push and pull packaged charts
Go toolchain1.22+ (optional)Only needed if you build Helm from source

Helm 4.3.x supports Kubernetes 1.34.x through 1.37.x under the project’s n-3 compatibility policy, meaning a Helm build compiled against a given Kubernetes client library stays compatible with that version and the three prior minor releases. If your cluster runs something older than 1.34, either upgrade the cluster or pin to an earlier Helm 4 patch that matches your version skew table on helm.sh before you continue.

Step 1: Install Helm 4.3.0

Pick whichever installation path fits your OS. All of these pull the current stable release unless you pin a version explicitly.

# macOS (Homebrew)
brew install helm

# Windows (Winget)
winget install Helm.Helm

# Windows (Chocolatey)
choco install kubernetes-helm

# Linux (installer script, grabs latest stable)
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

# Fedora 35+
sudo dnf install helm

Notice the installer script filename itself changed to get-helm-4, a small but telling sign of where the project’s default now sits. Confirm the install worked and check which version you’re actually running:

$ helm version
version.BuildInfo{Version:"v4.3.0", GitCommit:"a1f9c3e", GitTreeState:"clean", GoVersion:"go1.23.4"}

If you see a v3.x string instead and you meant to install Helm 4, check your PATH for an older binary shadowing the new one. Package managers sometimes leave a stale symlink behind after an upgrade.

Step 2: Point Helm at Your Kubernetes Cluster

Helm 4, like Helm 3 before it, reads your existing kubeconfig. There’s no separate server-side component to install, no Tiller, no extra RBAC bootstrap step. Whatever context kubectl currently points to is the cluster Helm will act on.

# Confirm kubectl is talking to the right cluster
kubectl config current-context
kubectl get nodes

# Confirm Helm can see the same cluster
helm list --all-namespaces

An empty table back from helm list is expected on a fresh cluster. It means Helm can reach the API server and there are simply no releases installed yet. If you get a connection error here instead, fix that before moving on. Every later step depends on this connection working.

Step 3: Add a Chart Repository and Find a Chart

Charts live in repositories, either traditional HTTP-based Helm repos or OCI registries. Add a well-known one and search it locally before installing anything.

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo bitnami/redis

For a broader search across every publisher, Artifact Hub indexes charts from thousands of repositories and lets you compare maintainers, verify signatures, and read values documentation before you commit to a chart. Treat it as your first stop when you don’t already know which repo to add. Never install a chart from a source you can’t identify. A chart’s templates run with whatever permissions your kubeconfig grants, so an untrusted chart is effectively untrusted code with cluster access.

Before running helm install against any third-party chart, read through its values.yaml at minimum, and ideally the templates too. Look specifically for anything that creates RBAC ClusterRoles, mounts the host filesystem, or runs privileged containers, since those are the permission levels most worth double-checking before you grant them to code you didn’t write.

Step 4: Install Your First Chart

Run a real helm install against a public chart before you build anything custom. This confirms your cluster, storage class, and networking all work together.

$ helm install my-redis bitnami/redis --namespace demo --create-namespace
NAME: my-redis
LAST DEPLOYED: Wed Sep  9 14:02:11 2026
NAMESPACE: demo
STATUS: deployed
REVISION: 1
TEST SUITE: None

Every helm install creates a release, a tracked, named instance of a chart with a specific set of values. You can install the same chart multiple times under different release names in the same or different namespaces, and Helm keeps each one’s history independent.

Step 5: Inspect and Verify What Helm Deployed

Don’t trust the “deployed” status alone. Check what actually landed in the cluster.

helm status my-redis -n demo
helm get values my-redis -n demo
kubectl get pods -n demo
kubectl get svc -n demo

helm get values shows only the values you overrode, not the full merged config. To see everything the chart resolved to, including defaults, add --all. When a pod sits in Pending or CrashLoopBackOff right after install, that’s a cluster or image problem, not a Helm problem. Helm’s job ends the moment it hands the rendered manifests to the API server.

Step 6: Understand Helm Chart Anatomy

Before you write your own chart, know what a chart actually is.

“Helm uses a packaging format called charts. A chart is a collection of files that describe a related set of Kubernetes resources.”

Helm documentation, helm.sh

A chart is nothing more than a directory with a required structure. Three pieces matter most.

Chart.yaml

Metadata about the chart itself: name, version, appVersion, and dependencies. The version field tracks the chart’s own release number and must bump on every change you publish. The appVersion field is just an informational label for the app version bundled inside, and Helm never uses it for compatibility checks.

values.yaml and templates/

values.yaml holds the default configuration. The templates/ directory holds Go-templated Kubernetes manifests that reference those values with syntax like {{ .Values.replicaCount }}. Helm renders every file in templates/ by merging your values into these templates, then sends the resulting plain YAML to the Kubernetes API. This is the same mechanism the Kubernetes blog described when Helm first introduced chart-based templating for the project.

Step 7: Scaffold a Custom Chart

Now build something real. This guide deploys notes-api, a small Node.js REST service, as a complete working example. Start by scaffolding the chart skeleton instead of writing every file by hand.

$ helm create notes-api
Creating notes-api

$ tree notes-api
notes-api/
├── Chart.yaml
├── charts/
├── templates/
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests/
│       └── test-connection.yaml
└── values.yaml

helm create generates a working chart for a generic web app, not empty placeholders. It’s meant to be trimmed and edited, not treated as a fixed template. Delete anything you don’t need (the HPA template, for instance, if you’re not autoscaling yet) rather than leaving dead files in the chart.

Step 8: Customize Values and Override Defaults

Edit Chart.yaml and values.yaml to describe your actual service.

# Chart.yaml
apiVersion: v2
name: notes-api
description: A small REST API for storing notes
type: application
version: 0.1.0
appVersion: "1.3.0"

# values.yaml (excerpt)
replicaCount: 2
image:
  repository: ghcr.io/example-org/notes-api
  tag: "1.3.0"
  pullPolicy: IfNotPresent
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi
service:
  type: ClusterIP
  port: 8080

Never hardcode environment-specific values directly into templates/. Push everything that changes between dev, staging, and production into values.yaml, then override it at install time with a separate file per environment.

# values-prod.yaml
replicaCount: 4
resources:
  limits:
    cpu: "1"
    memory: 512Mi

helm install notes-api ./notes-api -f values-prod.yaml -n production

Values Precedence: Files, –set, and Chart Defaults

Helm merges values from several sources, and knowing the order they win in saves a lot of confusion later. From lowest to highest priority: the chart’s own values.yaml defaults, then any subchart values, then -f files in the order you list them on the command line, then individual --set flags, which always win over everything else.

# Layer a base file, an environment file, and a one-off override
helm upgrade notes-api ./notes-api \
  -f values.yaml \
  -f values-prod.yaml \
  --set image.tag=1.3.2 \
  -n production

That last --set image.tag=1.3.2 overrides whatever tag either values file specified, even if values-prod.yaml comes later in the command. This is useful for a hotfix deploy where you don’t want to edit and re-commit a file just to bump one field, but resist the temptation to make --set your default workflow. Flags don’t show up in Git history the way a values file does, so six months later nobody can tell why a release is running a different tag than the file in the repo suggests. Reserve --set for genuine one-off overrides and CI-injected values like build-specific image digests.

The Complete notes-api Deployment Template

The scaffolded templates/deployment.yaml from helm create needs real probes and environment wiring before it’s production-ready. Here’s the edited template for notes-api, with values substituted through Go template syntax rather than hardcoded.

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "notes-api.fullname" . }}
  labels:
    {{- include "notes-api.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "notes-api.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "notes-api.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

Two details here matter beyond just filling in the blanks. First, the readiness and liveness probes point at a dedicated /healthz route rather than the app’s main endpoint, so a slow database query doesn’t get mistaken for a dead pod. Second, resources renders straight from the values file with toYaml, which means anyone overriding CPU or memory limits in a values file doesn’t have to touch the template at all. That’s the entire point of separating templates from values: the template describes structure, the values file describes environment-specific numbers, and the two never need to change together.

Production-Ready Values: Ingress, TLS, and Autoscaling

A staging deployment can get away with a bare ClusterIP service. Production usually needs an externally reachable hostname, TLS termination, and a horizontal pod autoscaler so the service can absorb a traffic spike without a manual scale-up. Here’s what that looks like layered on top of the base values shown earlier.

# values-prod.yaml (extended)
ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: notes-api.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: notes-api-tls
      hosts:
        - notes-api.example.com

autoscaling:
  enabled: true
  minReplicas: 4
  maxReplicas: 12
  targetCPUUtilizationPercentage: 70

podDisruptionBudget:
  enabled: true
  minAvailable: 2

The autoscaling block only does anything useful because the resources.requests.cpu set earlier gives the HPA a baseline percentage to measure against. Without that request set, a target of 70% CPU utilization is 70% of nothing, and the autoscaler never actually triggers. The pod disruption budget matters just as much and gets skipped just as often. It tells the cluster to never voluntarily evict more than a set number of pods at once during node drains or cluster upgrades, which is the difference between a rolling Kubernetes upgrade being invisible to users and it taking your service briefly offline.

Step 9: Lint and Dry-Run Before You Ship

Two commands catch most mistakes before they reach the cluster.

helm lint ./notes-api
helm template notes-api ./notes-api -f values-prod.yaml | less
helm install notes-api ./notes-api --dry-run --debug -n production

helm lint checks structure and syntax, catching missing required fields or malformed YAML. helm template renders the manifests locally without touching the cluster, so you can read exactly what would get created. --dry-run goes one step further and validates against the live API server’s schema without persisting anything. Run all three as a habit, not just when something already broke.

Step 10: Upgrade and Roll Back Releases Safely

Once notes-api is live, changes go through helm upgrade, never a fresh helm install.

helm upgrade notes-api ./notes-api -f values-prod.yaml -n production

$ helm history notes-api -n production
REVISION  UPDATED                   STATUS      CHART            APP VERSION
1         Wed Sep  9 09:14:02 2026  superseded  notes-api-0.1.0  1.3.0
2         Wed Sep  9 15:41:37 2026  deployed    notes-api-0.1.1  1.3.1

Every upgrade creates a new revision without deleting the old one, which is exactly what makes rollback fast. If revision 2 breaks something, go back to the last known-good state in one command.

helm rollback notes-api 1 -n production

Rollback re-applies the manifests from that earlier revision. It does not restore data, undo database migrations, or revert anything outside what the chart itself manages. Treat it as a fast mitigation step, then investigate the real cause afterward.

Step 11: Test the Release Before You Call It Done

The scaffolded chart from Step 7 already includes a test hook under templates/tests/test-connection.yaml. Chart tests are Kubernetes Jobs or Pods, tagged with the helm.sh/hook: test annotation, that run on demand against a live release to confirm the app actually responds, not just that the pods report Running.

$ helm test notes-api -n production
NAME: notes-api
LAST DEPLOYED: Wed Sep  9 15:41:37 2026
NAMESPACE: production
STATUS: deployed
REVISION: 2
TEST SUITE:     notes-api-test-connection
Last Started:   Wed Sep  9 15:44:02 2026
Last Completed: Wed Sep  9 15:44:05 2026
Phase:          Succeeded

Extend the default test to hit a real endpoint, such as a health check route, rather than leaving the generic connectivity probe the scaffold ships with. Wire helm test into your deployment pipeline right after helm upgrade so a broken release fails the pipeline instead of sitting quietly in a “deployed” state that nobody catches until a user reports it.

Step 12: Package and Push a Chart to an OCI Registry

Helm charts can be stored the same way container images are, as OCI artifacts. This means one registry, one auth mechanism, for both your app image and the chart that deploys it.

helm package ./notes-api
# produces notes-api-0.1.1.tgz

helm registry login ghcr.io -u your-username
helm push notes-api-0.1.1.tgz oci://ghcr.io/example-org/charts

# install directly from the OCI registry later
helm install notes-api oci://ghcr.io/example-org/charts/notes-api --version 0.1.1

OCI-based distribution has been stable, established behavior across Helm 3.x and carried forward unchanged into Helm 4, so charts you push today keep working after you upgrade the CLI.

Sign and Verify Charts With Provenance Files

Pushing a chart to a registry doesn’t prove who built it. For anything beyond a personal project, sign the package and generate a provenance file so downstream users can verify integrity before they install.

# Generate a GPG key first if you don't already have one, then:
helm package ./notes-api --sign --key 'your-name' --keyring ~/.gnupg/secring.gpg

# Produces both files:
#   notes-api-0.1.1.tgz
#   notes-api-0.1.1.tgz.prov

# Consumers verify before installing
helm verify notes-api-0.1.1.tgz

The provenance file bundles a SHA256 checksum of the package with the Chart.yaml contents, then signs the whole thing with your PGP key. Anyone who installs the chart can confirm both that the archive wasn’t tampered with in transit and that it actually came from the key they trust. This matters most for internal platform teams distributing charts across an organization, where “which team published this and did anyone touch it” is exactly the question an auditor asks after an incident.

Wiring Helm Into a CI/CD Pipeline

Running these commands by hand works for learning the tool, but a real deployment pipeline should run lint, template, diff, and upgrade automatically on every merge to your main branch. Here’s a minimal GitHub Actions job that does exactly that.

# .github/workflows/deploy.yml
name: deploy
on:
  push:
    branches: [main]

jobs:
  helm-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: azure/setup-helm@v4
        with:
          version: 'v4.3.0'
      - name: Configure kubeconfig
        run: echo "${{ '{{' }} secrets.KUBECONFIG {{ '}}' }}" | base64 -d > $HOME/.kube/config
      - name: Lint and template
        run: |
          helm lint ./notes-api
          helm template notes-api ./notes-api -f values-prod.yaml
      - name: Upgrade
        run: |
          helm upgrade --install notes-api ./notes-api \
            -f values-prod.yaml \
            --set image.tag=${{ '{{' }} github.sha {{ '}}' }} \
            --namespace production \
            --wait --timeout 5m
      - name: Test
        run: helm test notes-api -n production

Two flags in that upgrade step carry more weight than they look like they do. --install makes the same command work for both the very first deploy and every one after it, so the pipeline doesn’t need separate logic for “does this release already exist.” --wait --timeout 5m blocks the pipeline until Kubernetes reports the new pods actually healthy, not just accepted, and fails the build if that doesn’t happen within five minutes. Skip --wait and your pipeline will report success the instant Helm submits the manifests, even if every new pod immediately crashes.

Common Pitfalls That Break Helm Deployments

Most Helm incidents trace back to a small set of repeat mistakes. None of these are exotic, and all of them are easy to avoid once you’ve been burned by them once.

  • Using a mutable image tag like “latest” inside a chart. Rollback re-deploys the same tag, which may now point to a different image than it did at that revision. Pin exact tags or digests so a rollback actually rolls back.
  • Skipping resource requests and limits. Without them, the scheduler can pack pods too densely, and one noisy workload can starve everything else on the node. It also makes horizontal pod autoscaling meaningless, since the HPA has no baseline to scale from.
  • Committing secrets straight into values.yaml. Values files usually end up in Git. Use Kubernetes Secrets, an external secrets operator, or a plugin built for this instead of plaintext credentials in a chart repo.
  • Forgetting to bump the chart version on every published change. Repository indexes are keyed on chart version, not appVersion. Ship a fix without bumping it and consumers who already cached the index won’t see the update.
  • Mixing Chart.yaml apiVersion v1 and v2 conventions. The dependencies field moved from a separate requirements.yaml under v1 to directly inside Chart.yaml under v2. Old tutorials still reference the v1 layout, and copying one in verbatim breaks helm dependency update.
  • Installing directly to production without a dry run. A typo in a values path fails silently in many templates rather than throwing an error, so the chart deploys with an unintended default instead of your override.
  • Treating namespaces as optional. Omitting -n silently targets whatever namespace your current kubeconfig context defaults to, which is an easy way to install a staging release into production by accident.
  • Running helm upgrade without ever running helm diff first. Without a preview of what’s actually changing between revisions, a routine upgrade can quietly recreate resources that didn’t need to change, causing unnecessary pod restarts.

Troubleshooting: 8 Helm Errors and How to Fix Them

Most Helm failures show up as one of a handful of error strings. Knowing what they actually mean before you start debugging saves the time you’d otherwise spend chasing the wrong layer of the stack.

Error messageLikely causeFix
cannot re-use a name that is still in useA release with that name already exists, possibly in a failed stateRun helm list -a -n <ns>, then helm uninstall the stale release or pick a new name
another operation is in progressA previous install/upgrade was interrupted and left a lockWait for it to finish, or run helm rollback to the last stable revision to clear the stuck state
forbidden: User cannot create resourceYour kubeconfig’s identity lacks RBAC permission for that resource kindGrant the ServiceAccount or user role permissions via a Role/RoleBinding, then retry
no matches for kind in versionThe chart targets a deprecated or removed Kubernetes API groupCheck the chart’s templates for old apiVersion strings and update them for your cluster’s version
Pods stuck in ImagePullBackOffWrong image repository, tag, or missing registry credentialsVerify image.repository/image.tag in values and confirm an imagePullSecret is attached
Kubernetes cluster unreachablekubeconfig context points to the wrong cluster or a dead endpointRun kubectl config current-context and kubectl cluster-info to confirm connectivity first
Rollback succeeds but old image still runsDeployment strategy or pod template hash didn’t actually changeConfirm the rolled-back revision truly differs; check with kubectl rollout status
401 Unauthorized on helm pushRegistry token expired or wrong scope for OCI pushRe-run helm registry login with a token that has write access to that repository path

When an error doesn’t match anything in that table, fall back to a standard debugging sequence rather than guessing. Run helm get manifest <release> -n <namespace> to see exactly what Helm actually submitted to the cluster, since that’s often different from what you expected the templates to render. Then check kubectl describe pod <pod-name> -n <namespace> for scheduling and image-pull events, and kubectl logs <pod-name> -n <namespace> for application-level errors. In that order, you move from “what did Helm ask for” to “what did the scheduler do with it” to “what did the app do once it started,” which covers the three layers where a deployment most commonly breaks.

Advanced Tips: Hooks, Dependencies, and Multi-Chart Setups

Once the basics work, a few features handle the cases a simple helm install doesn’t cover. None of these are needed for a first deployment, but each one solves a specific problem you’ll eventually run into as a chart matures and more people start depending on it.

Hooks let a chart run a Job at a specific point in its lifecycle, tagged with annotations like helm.sh/hook: pre-install or post-upgrade. Use them for database migrations that must finish before new pods start serving traffic, not for anything that needs to run on every reconcile.

Dependencies let one chart pull in others as subcharts. Declare them in Chart.yaml’s dependencies field, then run helm dependency update to fetch them into the charts/ directory. This is how a chart for a full application stack, as the Helm documentation itself describes it, bundles HTTP servers, databases, and caches into one installable unit.

Multi-environment orchestration is where plain Helm starts to strain. Helmfile lets you declare every release across every environment in one file and apply them together, which scales better than a folder of shell scripts once you’re past three or four environments. For secrets specifically, a plugin like helm-secrets paired with SOPS keeps encrypted values in Git instead of leaving plaintext credentials sitting in a values file.

Diffing before you apply closes the gap that --dry-run leaves open. The helm-diff plugin compares your pending upgrade against the currently deployed revision and prints exactly which fields would change, resource by resource, before anything touches the cluster.

helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade notes-api ./notes-api -f values-prod.yaml -n production

Run this as a required step in CI before any upgrade reaches production. A diff that shows an unexpected full pod recreation, instead of a small config change, is often the first sign that a values file merged wrong or a template regressed.

Helm 3 vs Helm 4: What Actually Changed

If you’ve run Helm 3 for years, here’s what changes and what doesn’t when you move to Helm 4.

AspectHelm 3 (v3.22.0)Helm 4 (v4.3.0)
Release status as of September 2026Final feature release; security fixes only through Feb 10, 2027Current stable line, active development on main
Kubernetes support window1.34.x – 1.37.x (v3.22.x)1.34.x – 1.37.x (v4.3.x)
Server-side component (Tiller)None (removed since Helm 3.0)None
Chart format (Chart.yaml apiVersion)v2v2, unchanged
OCI registry supportStableStable, carried forward unchanged
New feature developmentFrozen after 3.22.0Ongoing

Neither line brought back Tiller, and neither changed the core chart format, so a chart you built for Helm 3 should install cleanly under Helm 4 without rewriting templates. The practical difference is where new features and active support are headed going forward. Helm’s own repository states plainly that v4 is the current stable release, developed on the project’s main branch, while the v3 branch is now in maintenance mode only.

Frequently Asked Questions

Is Helm still relevant now that kubectl supports server-side apply?

Yes. Server-side apply solves conflict detection for individual manifests, but it doesn’t give you packaging, versioned releases, templating with reusable values, or one-command rollback. Helm and server-side apply solve different problems and are commonly used together.

Do I need to migrate from Helm 3 to Helm 4 right away?

Not urgently. Helm 3.22.0 keeps receiving security patches through February 10, 2027. But since it’s the final Helm 3 feature release, any new capability the project ships from here only lands in the Helm 4 line, so plan the migration before that support window closes.

What Kubernetes version do I need for Helm 4.3.0?

Helm 4.3.x officially supports Kubernetes 1.34.x through 1.37.x under the project’s n-3 compatibility policy. Using it against a much newer or older cluster than that range isn’t recommended, since Helm makes no forward compatibility guarantees.

Does Helm 4 bring back Tiller?

No. Tiller, the server-side component removed in Helm 3.0 for security reasons, stays gone in Helm 4. Helm still runs entirely as a client that talks to the Kubernetes API using your existing kubeconfig permissions.

How do I find trustworthy public Helm charts?

Start with Artifact Hub, which indexes charts across repositories and shows maintainer identity, security scan results where available, and values documentation. Prefer charts maintained by the project itself or a recognized vendor over unverified community forks, since a chart’s templates run with your cluster’s permissions.

Can I use Helm charts in an air-gapped cluster with no internet access?

Yes. Package the chart locally with helm package, mirror the container images and chart artifacts into an internal registry, then install with helm install ./mychart.tgz or from your internal OCI registry. Nothing about Helm’s install path requires reaching the public internet at install time.

What’s the difference between a Helm chart and a Kustomize overlay?

Kustomize patches existing plain YAML without a templating language, which keeps things simple but limits reuse across very different environments. Helm charts use Go templating and a values system built for parameterizing one chart across many use cases, plus versioned releases and rollback. Some teams use both together, applying Kustomize patches on top of rendered Helm output.

How do I safely uninstall a Helm release without losing data?

Run helm uninstall <release> -n <namespace>, but check first whether the chart’s PersistentVolumeClaims have a retain policy or get deleted alongside the release. Some charts expose a values flag to keep storage around after uninstall specifically to prevent accidental data loss. Read the chart’s README before you assume the default behavior, and for anything holding production data, take a manual snapshot before you run the uninstall command regardless of what the chart claims it does.

How many revisions does Helm keep, and can I clean up old ones?

By default Helm keeps the full release history, though most installs cap it at 10 revisions via --history-max during install or upgrade to avoid unbounded growth of the Secrets or ConfigMaps Helm uses for storage. Old revisions cost almost nothing at rest, so there’s rarely a strong reason to trim them further unless you’re operating at very high release velocity on a single service.

Can I convert existing raw Kubernetes YAML into a Helm chart?

Yes, and it’s usually less work than starting from scratch. Run helm create to get the standard directory layout, then drop your existing manifests into templates/, replacing the hardcoded values that differ per environment with template references like {{ .Values.replicaCount }}. Move those extracted values into values.yaml. Start with the fields most likely to change between environments, such as replica counts, image tags, and hostnames, rather than trying to templatize every field on day one. A chart with five parameterized fields that actually gets used beats one with fifty fields nobody remembers the purpose of.