Helm installs and updates applications in Kubernetes using packages called charts. If your team uses Helm 3 in deployment scripts, a laptop upgrade to Helm 4 is only one part of the change: build jobs, plugins and controllers may use their own Helm versions.

Helm 4 is the current major version. The updated Helm support notice extends Helm 3 security fixes through 10 February 2027. Start by finding every place Helm runs, then test the same chart with the new tool before changing the application itself. A chart that renders successfully can still fail during installation or recovery.

The checklist below prepares that upgrade. The later recorded experiment shows one small Helm 3 release upgraded and rolled back with Helm 4; it does not establish compatibility for every chart or integration.

Inventory the execution paths

Find developer CLI versions, CI images, deployment scripts, Terraform providers, GitOps controllers, plugins and post-renderers. Record which component owns each release. Upgrading a developer workstation does not upgrade an embedded Helm SDK or a controller. Check each integration's published compatibility instead of assuming it follows the CLI.

Build a representative acceptance set

  1. Choose one simple stateless release, one release with hooks and one with CRDs or persistent data.
  2. Save the chart reference or digest, values, release name, namespace and effective configuration without exposing credentials.
  3. Compare rendered objects and exercise install, upgrade and a deliberately failing deployment in a disposable environment.
  4. Check private OCI authentication, dependencies, post-rendering, plugin installation and exit-status handling.
  5. Observe timeout and failure behavior in the same automation runner used for production.

Separate chart changes from tool changes

First hold the chart and application versions constant while changing Helm. Then evaluate application upgrades independently. Otherwise a changed default image, chart schema or CRD can be mistaken for a Helm regression. Track chart version and app version separately.

Rollback boundaries

Keep the previous deployment runner and known-good artifact available while testing. Do not assume switching the CLI binary back reverses every release-state or application change. Helm rollback is not a database rollback, and it does not automatically reverse external effects of hooks. Stateful releases need their own recovery evidence.

Promote the tested runner after the acceptance set passes, then widen by release class. Record the actual versions and outcomes in the change log. This page supplies the review plan; it does not claim that Kubedex has tested every Helm 4 plugin or controller integration. For concepts, start with the Helm guide.

What changes to review in Helm 4

Helm's overview documents a new plugin architecture, post-renderer integration changes, registry-login input changes and renamed CLI flags. Review scripts that use post-renderers, --atomic or --force; the documented replacements are --rollback-on-failure and --force-replace. An existing Helm 3 release keeps its previous apply method by default when upgraded by Helm 4, while a new Helm 4 installation defaults to server-side apply. Test these as different cases rather than assuming every release changes field ownership immediately.

Reproduce the bounded test

On 9 October 2026, Kubedex tested Helm 3.22.0 and 4.3.0 against Kubernetes 1.37.0 in a single Linux arm64 kind node (kind 0.33.0). Both downloaded Helm versions use Kubernetes client 1.37. Their official archive checksums were verified. A preinstalled Helm 4.1.1 binary was not used because its Kubernetes client was older than this test cluster.

The downloadable fixture contains a tiny chart, pinned image digest, instructions and the observed results. Its Deployment serves a ConfigMap value over HTTP. It has no database, hooks or CRDs. Read the fixture README before running it and use a disposable cluster. Every cluster command below carries an explicit kubeconfig and context through the Bash arrays.

fixture_config=/absolute/path/to/disposable-kubeconfig
fixture_context=your-disposable-context
helm3=/absolute/path/to/helm-v3.22.0
helm4=/absolute/path/to/helm-v4.3.0
fixture_namespace=kubedex-helm-upgrade-fixture
k=(kubectl --kubeconfig "$fixture_config" --context "$fixture_context")
h=(--kubeconfig "$fixture_config" --kube-context "$fixture_context" --namespace "$fixture_namespace")
"${k[@]}" get nodes
"${k[@]}" create namespace "$fixture_namespace"
"$helm3" install upgrade-demo ./chart "${h[@]}" --wait --timeout 120s
"$helm4" upgrade upgrade-demo ./chart "${h[@]}" --set message=after-upgrade --wait --timeout 120s

Observed: revision 1 was deployed and returned before-upgrade; revision 2 was deployed and returned after-upgrade. The fixture README explains the localhost port-forward used to inspect the response after each step. Reconnect the port-forward if its selected pod is replaced.

Test a failure you can recognize

This command deliberately changes the readiness check to a nonexistent path. It is expected to fail and should not be pasted into a production release:

"$helm4" upgrade upgrade-demo ./chart "${h[@]}" --set message=failed-upgrade --set readinessPath=/missing --wait --timeout 20s

Observed exit status: 1. The error included the following lines, and the release revision was marked failed:

Error: UPGRADE FAILED: resource Deployment/kubedex-helm-upgrade-fixture/upgrade-demo not ready. status: InProgress, message: Pending termination: 1
context deadline exceeded

Verify application recovery after rollback

"$helm4" rollback upgrade-demo 2 "${h[@]}" --wait --timeout 120s
"$helm4" history upgrade-demo "${h[@]}"
"$helm4" rollback upgrade-demo 1 "${h[@]}" --wait --timeout 120s

Observed: both rollback commands succeeded. Rollback to revision 2 created revision 4 and restored after-upgrade; rollback to the original Helm 3 revision created revision 5 and restored before-upgrade.

The first complete test exposed a subtlety: Helm reported rollback success while the selected ready pod still served failed-upgrade. The mounted ConfigMap then converged to after-upgrade after 52.9 seconds. This is an observed delay in this environment, not a promised upper bound. Kubernetes projects ConfigMap changes asynchronously. The automated test therefore checked the served content with a bounded wait instead of treating a successful Helm command as sufficient recovery evidence.

The final digest-pinned run and its command output are in the observed-results file. After the experiment, stop port-forwarding and delete only the namespace you created:

"${k[@]}" delete namespace "$fixture_namespace" --wait=true --timeout=120s

This establishes a simple Helm 3-created release's upgrade and rollback behavior under Helm 4. It does not certify plugins, embedded SDKs, private OCI authentication, stateful recovery, CRD changes or production performance. Use the acceptance set above to extend this evidence to the release classes you actually operate.

Sources & further reading

  1. Updated Helm 3 support schedule
  2. Helm 4 overview
  3. Helm support and Kubernetes skew
  4. Kubernetes ConfigMap update behavior

Spotted something that needs another look?

Help improve this page →