Join us

Helm Cheat Sheet: Everything You Need to Know to Start Using Helm

TL;DR:

Helm is the package manager Kubernetes was missing. It lets you package applications and their dependencies into charts, deploy them as versioned releases, and manage installs, upgrades, and rollbacks in a consistent and repeatable way. This post walks through what Helm is, how to install it, and the core commands you will use day to day.


Disclaimer: This introductory post is adapted from my guide Helm in Practice, which explores Helm from fundamentals to real-world production usage. You can get your copy here.

What is Helm?

The simple answer is that Helm is a package manager for Kubernetes.

Think of it as apt for Debian systems, yum for Red Hat systems, npm for Node.js, pip for Python, or Ansible for server automation. With its flexible templating system, it makes defining, packaging, configuring, installing, and upgrading applications on Kubernetes consistent, repeatable, and traceable.

It allows you to define and package an application and its dependencies into a single logical unit called a chart that can easily be shared and deployed on different clusters and environments.

Definitions are always complicated to grasp without examples. This post will introduce you to Helm by walking you through its installation and usage.

How to Install Helm

There are different ways to install Helm, depending on your operating system and preferences. However, the common approach is to install Helm on your local machine and then use it to manage applications on your Kubernetes clusters.

ℹ️ Helm should be installed in your local machine, management server ..etc not in your cluster VMs.

Here are the most common methods to install Helm:

Using a binary release:

Helm is an open source project hosted on GitHub. You can download the latest binary release for your operating system from the Helm GitHub releases page

To install the version 4.0.0 on Linux, you should go to the releases page, choose the appropriate tarball file, and run a series of commands like these:

# Export the URL for the Helm binary
url=https://get.helm.sh/helm-v4.0.0-linux-amd64.tar.gz

# Download the Helm binary
curl -LO $url

# Extract the tarball
tar -zxvf helm-v4.0.0-linux-amd64.tar.gz

# Move the Helm binary to a directory in your PATH
mv linux-amd64/helm /usr/local/bin/helm

# Verify the installation
helm version

# Remove the downloaded files
rm helm-v4.0.0-linux-amd64.tar.gz
rm -rf linux-amd64

Run the above commands to install Helm. After the installation, you can verify that Helm is installed correctly by running helm version, which should display the version of Helm you just installed.

Alternative Installation Methods

Using a script:

Helm provides an installation script that automates the download and installation process. You can download and run the script with the following commands:

# Export the URL for the Helm installation script
url=https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-4

# Download the Helm installation script
curl -fsSL $url -o get_helm.sh

# Run the Helm installation script
bash get_helm.sh --version v4.0.0

# Remove the installation script
rm get_helm.sh

Using a package manager:

If you prefer using the package manager for your operating system, you can follow one of the following methods:

# macOS with Homebrew
brew install helm

# Windows with Chocolatey
choco install kubernetes-helm

# Windows with Scoop
scoop install helm

# Debian/Ubuntu with apt
apt-get install curl gpg apt-transport-https --yes

curl -fsSL https://packages.buildkite.com/helm-linux/helm-debian/gpgkey | \
  gpg --dearmor | \
  tee /usr/share/keyrings/helm.gpg > /dev/null

echo "deb [signed-by=/usr/share/keyrings/helm.gpg] https://packages.buildkite.com/helm-linux/helm-debian/any/ any main" | \
  tee /etc/apt/sources.list.d/helm-stable-debian.list

apt-get update

apt-get install helm

# Fedora 35+ with dnf
dnf install helm

# FreeBSD with pkg
pkg install helm

# Linux with Snap
snap install helm --classic

From source:

This method is recommended for developers who want to contribute to Helm or build it from the latest source code. You need to have Go installed on your system to build Helm from source. You can clone the Helm repository from GitHub and run the following commands:

# Clone the Helm repository
git clone https://github.com/helm/helm.git

# Change to the Helm directory
cd helm

# Build Helm from source
make

# Move the Helm binary to a directory in your PATH
mv bin/helm /usr/local/bin/helm

# Verify the installation
helm version

# Change back to the previous directory
# and remove the cloned repository if you don't need it anymore
cd .. && rm -rf helm

Charts, Releases, and Repositories

A chart is a collection of files that describe a related set of Kubernetes resources. A single chart can define a simple application, like a Redis instance, or a complex one, like a complete web application stack with a application server, a database, and a caching layer.

When you install a chart in a Kubernetes cluster, this creates a new release of that chart. A release is a specific instance of a chart running in a Kubernetes cluster. You can have multiple releases of the same chart in a cluster, each with its own configuration and state. The name of the release is specified during the installation process and should be unique within the target namespace of the cluster.

A repository, on the other hand, is a place where charts are stored, versioned, and shared. A repository can contain multiple charts, and as a user, you can add repositories to your Helm client to access and install charts from them. One of the most popular public repositories is the Artifact Hub, which hosts a wide variety of charts for different applications and services.

A chart is a collection of files that is organized as follows (an example from a WordPress chart):

wordpress/
  # A YAML file containing information
  # about the chart
  Chart.yaml
  # OPTIONAL: A plain text file containing
  # the license for the chart
  LICENSE
  # OPTIONAL: A human-readable README file
  README.md
  # The default configuration values for this chart
  values.yaml
  # OPTIONAL: A JSON Schema for imposing a
  # structure on the values.yaml file
  values.schema.json
  # A directory containing any charts
  # upon which this chart depends.
  charts/
  # Custom Resource Definitions
  crds/
  # A directory of templates that,
  # when combined with values,
  # will generate valid Kubernetes manifest files.
  templates/
  # OPTIONAL: A plain text file containing
  # short usage notes
  templates/NOTES.txt

This is a quick description of each file and directory:

  • Chart.yaml: A YAML file containing information and metadata about the chart, such as its name, version, description, and dependencies

  • LICENSE: A plain text file containing the license for the chart like MIT, Apache 2.0, or GPL.

  • README.md: A human-readable README file.

  • values.yaml: The default configuration values for this chart. These values can be overridden during installation or upgrade to customize the behavior of the installed application.

  • values.schema.json: A JSON Schema for imposing a structure on the values.yaml file. For example, wordpressUsername must be a string, replicaCount must be an integer, and so on. Helm validates the schema on helm install and helm upgrade, and it fails early if the values don't match. People often think it's optional metadata, but in practice it enforces guardrails and provides better user experience.

  • charts/: This folder contains packaged subcharts when they are vendored locally. Dependencies declared in Chart.yaml only appear here after running helm dependency update, unless they're already vendored (included directly in the charts/ directory).

  • crds/: A CRD is a Kubernetes resource that allows you to define your own custom resource types. CRD, or Custom Resource Definition, files are usually needed when your chart depends on custom resources provided by an operator or another chart. CRDs placed here are always installed before templates which is normal since custom resources need to exist before you can create instances of them. However, they are never templated. Helm installs them once and does not manage updates or deletes unless you explicitly handle them.

  • templates/: A directory of templates that, when combined with values, will generate valid Kubernetes manifest files. It usually contains definitions for various Kubernetes resources, such as:

  • deployment.yaml: A template for creating a Deployment for the application
  • service.yaml: A template for creating a Service for exposing the application.
  • ingress.yaml: A template for creating an Ingress for routing external traffic to the application.
  • _helpers.tpl: A file containing helper templates (functions) that can be used throughout the other templates.
  • And more depending on the chart
  • NOTES.txt: A plain text file containing short usage notes that are displayed to the user after installing or upgrading a chart.

Helm Plugins

Helm plugins are extensions that augment the functionality of Helm. They can add new commands, modify existing ones, or integrate with other tools and services. Helm categorizes plugins into three types:

  • CLI plugins: These plugins add new commands to the Helm CLI. For example, a plugin might add a command for linting charts or for managing chart repositories.

Example: helm-diff adds a helm diff command that shows the differences between Helm releases. For example, you can use helm diff upgrade my-release stable/postgresql --values values.yaml to see what changes would be applied if you upgraded the my-release release of the PostgreSQL chart with the specified values.

  • Getter plugins: These plugins extend Helm's ability to fetch charts or even other plugins from location types not natively supported by Helm. For example, a plugin might allow Helm to fetch charts from a custom storage backend like an S3 bucket or a private Git repository.

Example: helm-s3 allows you to use an Amazon S3 bucket as a Helm chart repository.

  • Post-renderer plugins: These plugins modify the rendered Kubernetes manifests before they are applied to the cluster. For example, a plugin might add additional labels or annotations to all resources or perform custom transformations on the manifests.

Example: helm-secrets allows you to manage secrets in Helm charts using tools like Mozilla SOPS. For example, you can use helm secrets install my-release ./my-chart to install a chart with encrypted secrets that are decrypted on-the-fly during the installation process.

To install a Helm plugin, you can use the helm plugin install command followed by the URL of the plugin's repository. For example, to install the helm-diff plugin, you would run:

helm plugin install https://github.com/databus23/helm-diff \
    --verify=false

To list the installed plugins, you can use the helm plugin list command:

helm plugin list

To remove a plugin, you can use the helm plugin remove command followed by the name of the plugin:

helm plugin remove diff

You can find more Helm plugins on the Artifact Hub or by searching GitHub for Helm plugins. Here is a list of some popular Helm plugins:

  • Helm Unittest - A plugin that provides a BDD (Behavior Driven Development) styled unit test framework for Kubernetes Helm charts.
  • Helm Diff - A Helm plugin that shows a diff explaining what a helm upgrade would change
  • Helm Secrets - A helm plugin that help manage secrets with Git workflow and store them anywhere using Mozilla SOPS.
  • Helm Blob - A Helm Plugin that allows you to manage private helm repositories on blob storage(Azure Blob, GCS, S3)
  • Helm Schema - A plugin to auto-generate jsonschema files for helm charts.
  • Helm Schema Gen - A Helm plugin to generate jsonschemas from helm charts.
  • Helm Starter - A Helm plugin for managing Helm starters. Helm starters are used by the helm create command to customize the default chart. For example, an Istio starter can create VirtualService and DestinationRule objects, in addition to the standard Service and Deployment objects.
  • Helm Dt - Helm Distribution plugin is is a set of utilities and Helm Plugin for making offline work with Helm Charts easier. It is meant to be used for creating reproducible and relocatable packages for Helm Charts that can be moved around registries without hassles. This is particularly useful for distributing Helm Charts into airgapped environments.
  • Helm Datree - A plugin to prevent Kubernetes misconfigurations by ensuring that Helm charts follow best practices as well as your organization’s policies
  • Helm Release - A Helm plugin that pulls (re-creates) helm Charts from deployed releases, and updates values of deployed releases without the chart.
  • And more!

Helm Environment Variables

Helm uses some environment variables to configure its behavior and settings. Using the following command, you can see the list of what's currently set in your environment:

helm env

You should see an output similar to this:

# Binary name
HELM_BIN="helm"

# The path to Helm configuration files
HELM_CONFIG_HOME="/root/.config/helm"
# Registry-related environment variables
HELM_REGISTRY_CONFIG="/root/.config/helm/registry/config.json"
# Repository-related environment variables
HELM_REPOSITORY_CONFIG="/root/.config/helm/repositories.yaml"
# Cache-related environment variables
HELM_CACHE_HOME="/root/.cache/helm"
# Repository cache path
HELM_REPOSITORY_CACHE="/root/.cache/helm/repository"
# Data-related environment variables
HELM_DATA_HOME="/root/.local/share/helm"
# Plugins path
HELM_PLUGINS="/root/.local/share/helm/plugins"

# Debugging and output
HELM_DEBUG="false"

# Kubernetes API server URL
HELM_KUBEAPISERVER=""
# Kubernetes CA certificate file path
HELM_KUBECAFILE=""
# Kubernetes context to use
HELM_KUBECONTEXT=""
# Skip TLS verification
HELM_KUBEINSECURE_SKIP_TLS_VERIFY="false"
# TLS server name override
HELM_KUBETLS_SERVER_NAME=""
# Kubernetes bearer token
HELM_KUBETOKEN=""

# Group and user impersonation
HELM_KUBEASGROUPS=""
HELM_KUBEASUSER=""

# Default kubernetes Namespace
HELM_NAMESPACE="default"

# Kubernetes API rate limiting: Burst limit
HELM_BURST_LIMIT="100"
# Kubernetes API rate limiting: Queries per second
HELM_QPS="0.00"

Understanding Helm Chart Installation and Management

Charts can be installed from 4 main sources:

  • A remote chart repository (e.g., https://charts.bitnami.com/bitnami)
  • A remote OCI-compliant registry (e.g.,: oci://ghcr.io/my-charts)
  • A local chart directory (on your disk)
  • A local chart archive (a tarball file, e.g., my-chart-1.0.0.tgz)

You have the freedom to choose the source that best fits your needs but usually, charts are installed from remote repositories/registries. However, as a developer, you might often work with local chart directories or archives during the development and testing phases.

The general syntax for installing a chart is:

helm install [RELEASE_NAME] [CHART] [flags]

When you have new versions or new configurations to apply to an existing release, you can use the helm upgrade command. The general syntax is:

helm upgrade [RELEASE_NAME] [CHART] [flags]

You can also use helm upgrade --install, which will install the chart if the release does not already exist, or upgrade it if it does.

helm upgrade --install [RELEASE_NAME] [CHART] [flags]

Or:

helm upgrade -i [RELEASE_NAME] [CHART] [flags]

Let's see a few examples and variations of this command.

Installing from a Specific Repository

The general syntax for installing a chart from a specific repository is:

helm install [RELEASE_NAME] [REPO_NAME/CHART_NAME] [flags]

Here is an example of installing the Bitnami WordPress chart from the latest version available in the Bitnami repository:

helm repo add bitnami https://charts.bitnami.com/bitnami

helm repo update

helm upgrade -i \
  my-wordpress \
  bitnami/wordpress

# List the pods
kubectl get pods

Using a Chart with Custom Configurations

If you want to customize the installation of a chart by changing specific configuration values, you can use the --set flag followed by the key-value pairs you want to modify. The general syntax is:

helm install [RELEASE_NAME] [CHART] --set key1=value1,key2=value2

As an example, let's upgrade the previous WordPress installation to use a LoadBalancer service with custom ports:

  • The port 8000 as the service port for HTTP traffic (instead of the default 80 already used by the Ingress controller)
  • The port 8443 as the service port for HTTPS traffic (instead of the default 443 already used by the Ingress controller)
  • The node port 30008 as the port to open on each node for HTTP traffic
  • The node port 30009 as the port to open on each node for HTTPS traffic
# Complete reset - this will delete everything and start fresh
helm uninstall my-wordpress --ignore-not-found
kubectl delete pvc --all --ignore-not-found
sleep 10

# Install with custom configurations
helm upgrade -i \
  my-wordpress \
  bitnami/wordpress \
  --set service.type=LoadBalancer \
  --set service.ports.http=8000 \
  --set service.ports.https=8443 \
  --set service.nodePorts.http=30008 \
  --set service.nodePorts.https=30009

# Test the installation
EXTERNAL_IP=$(\
  kubectl get svc \
  --namespace default \
  my-wordpress \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'\
)

# Verify pods
kubectl get pods

# Verify services
kubectl get svc

# Access WordPress
echo "WordPress URL: http://$EXTERNAL_IP:8000/"
echo "WordPress URL (HTTPS): https://$EXTERNAL_IP:8443/"

service.type, service.ports.http, service.ports.https, service.nodePorts.http, and service.nodePorts.https are configuration keys defined in the values.yaml file of the WordPress chart. You can view all the configuration options available by running the following command:

helm show values bitnami/wordpress | yq --yaml-output

You should see the following output snippet:

[...]
service:
  type: LoadBalancer # > Type of service to create
  ports:
    http: 80 # > HTTP service port
    https: 443 # > HTTPS service port
  httpsTargetPort: https
  nodePorts:
    http: '' # > Node port for HTTP
    https: '' # > Node port for HTTPS
  sessionAffinity: None
  sessionAffinityConfig: {}
  clusterIP: ''
  loadBalancerIP: ''
  loadBalancerSourceRanges: []
  externalTrafficPolicy: Cluster
  annotations: {}
  extraPorts: []
[...]

Using a Chart with a Custom Configuration File

If you have 1 or 2 configurations to define, using the --set flag is a good option. However, if you have tens of values to change, the --set flag can become cumbersome and error-prone. In such cases, it's better to create a custom configuration file (e.g., values.yaml) and use the --values flag to specify it during installation.

helm upgrade -i [RELEASE_NAME] [CHART] --values values.yaml

In our case, if we want to achieve the same configuration as before using a custom values.yaml file, we can create the file with the following content:

cd $HOME && cat <<EOF > values.yaml
service:
  type: LoadBalancer
  ports:
    http: 8000
    https: 8443
  nodePorts:
    http: 30008
    https: 30009
EOF

Then, the installation command becomes shorter and cleaner:

helm upgrade -i \
  my-wordpress \
  bitnami/wordpress \
  --values $HOME/values.yaml

# Clean up
rm $HOME/values.yaml

# Verify the installation
kubectl get pods

What happens at this point is that Helm will merge the default configuration in the values.yaml file of the chart with the custom configuration provided in our values.yaml file. Our custom values will override the default ones.

Install a Chart from a Local Tarball

If you have a chart packaged as a tarball (a .tgz file), you can install it directly using the following syntax:

helm upgrade -i [RELEASE_NAME] [PATH_TO_TARBALL] [flags]

Same as before, we can add custom configurations using the --set flag or a custom configuration file (--values flag).

Let's try this out. First, download the WordPress chart as a tarball:

cd $HOME
# Remove any existing wordpress directories or tarballs
rm -rf wordpress*

# Pull the chart
helm pull bitnami/wordpress

# Check the downloaded file
ls -lh wordpress-*.tgz

# Install the chart from the local tarball
helm upgrade -i \
  my-wordpress \
  ./wordpress-*.tgz

# Verify the installation
kubectl get pods

# Clean up
rm wordpress-*.tgz

Installing a Chart from a Local Directory

Installing a chart from a local directory is usually done during the development and testing phases. The general syntax is:

helm install [RELEASE_NAME] [CHART_DIRECTORY] [flags]

In our case, if we have the WordPress chart downloaded and extracted in a directory called wordpress-chart, we can install it as follows:

helm install \
  my-wordpress \
  ./wordpress-chart

It's possible to use a custom configuration file when installing a chart from your disk. The general syntax is:

helm install [RELEASE_NAME] [CHART_REPO/CHART_NAME] --values values.yaml

Let's try this out. First, download and extract the WordPress chart:

cd $HOME
# Remove any existing wordpress directories or tarballs
rm -rf wordpress*

# Pull the chart
helm pull bitnami/wordpress

# Extract the chart
tar -xvf wordpress-*.tgz

# Install the chart from the local directory
helm upgrade -i \
  my-wordpress \
  $HOME/wordpress

# Verify the installation
kubectl get all -l app.kubernetes.io/instance=my-wordpress

Default Helm Labels

In our previous example, we ran the following command to verify the installation:

kubectl get all -l app.kubernetes.io/instance=my-wordpress

The label app.kubernetes.io/instance is one of the default labels that Helm adds to all the Kubernetes resources it creates. This label helps identify which resources belong to a specific Helm release.

There are other default labels automatically added by Helm as well:

  • app.kubernetes.io/name: The name of the chart (e.g., wordpress).
  • app.kubernetes.io/version: The version of the application (e.g., 5.8.1).
  • app.kubernetes.io/managed-by: The tool managing the deployment (e.g., Helm).
  • app.kubernetes.io/component: The component within the application (e.g., web, database, frontend, backend etc., depending on the chart).
  • app.kubernetes.io/part-of: The name of the larger application this component is part of (e.g., my-application).
  • helm.sh/chart: The name and version of the chart (e.g., wordpress-10.1.5).

These labels are created based on the information provided in the Chart.yaml file and the release name specified during installation. The following table summarizes what's created and how:

LabelRequired/OptionalDescriptionHow it's Set
app.kubernetes.io/nameRequiredLogical name of the application. Groups all components belonging to a single app.Usually set using {{ template "name" . }}, which typically resolves to .Chart.Name.
helm.sh/chartRequiredIdentifies which chart and chart version produced this resource. Useful for auditing and debugging.Helm sets this via {{ .Chart.Name }}-{{ .Chart.Version \| replace "+" "_" }} so labels remain valid.
app.kubernetes.io/managed-byRequiredIndicates the management tool (always Helm for Helm-managed objects).Automatically set to {{ .Release.Service }}, which equals "Helm".
app.kubernetes.io/instanceRequiredDistinguishes different deployments of the same chart (prod, staging, test, etc.).Set to {{ .Release.Name }}, the name passed during helm install/upgrade.
app.kubernetes.io/versionOptionalVersion of the underlying application (e.g., WordPress, Redis), not the chart version.Usually set using {{ .Chart.AppVersion }} from Chart.yaml.
app.kubernetes.io/componentOptionalIdentifies the component role: frontend, backend, database, worker, etc.Manually defined in chart templates or values.yaml.
app.kubernetes.io/part-ofOptionalGroups multiple charts/components under one larger system or “application”.Manually set by chart authors using a fixed value or {{ .Values.appName }}.

Below is a practical explanation of every template reference that appears in the table:

{{ template "name" . }}

This calls a named template defined in _helpers.tpl files. Many charts include a template like this:

{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

This template returns a canonical name for the chart, it's created by:

  • Check for a name override

The expression default .Chart.Name .Values.nameOverride means: If .Values.nameOverride is set and non-empty, use that value. Otherwise, use .Chart.Name.

  • Apply truncation

trunc 63 ensures the final name does not exceed 63 characters, the maximum allowed for many Kubernetes resource names (DNS label limit).

  • Remove trailing hyphens

trimSuffix "-" cleans up names that might end in a dash after truncation or templating.

  • Return the resolved chart name

The result is stored under the template name "name", which can be reused everywhere as: {{ template "name" . }}.

.Chart.Name:

The name of the chart, defined in Chart.yaml:

Chart.yaml:
  name: my-chart

.Chart.Version:

This is also defined in Chart.yaml and represents the chart version. It's used to track which version of the chart generated the manifest.

version: 28.0.2

.Chart.AppVersion:

In Chart.yaml, this field indicates the version of the application that the chart deploys. For example, if you're deploying WordPress, this would be the WordPress version.

Example from the Chart.yaml file:

appVersion: "6.9.0"

Helm doesn't understand or use this field automatically - chart authors choose to expose it as a label.

ℹ️ Do not confuse .Chart.Version and .Chart.AppVersion! The first is the chart version, the second is the application version. We can have multiple chart versions for the same application version (e.g., bug fixes, new features in the chart) but the underlying application remains the same. We can also deploy different application versions using the same chart version by overriding values.

.Release.Name:

This is a dynamic value that you won't find in any file. It represents the name of the Helm release and is specified at install time.

It's created when you run a command like:

# .Release.Name = my-wordpress
helm install my-wordpress ...

# .Release.Name = my-redis
helm install my-redis ...

.Release.Service:

Used for the label app.kubernetes.io/managed-by, this static value is usually set to "Helm" to indicate that Helm is managing the resource.

.Values.*:

Everything under this reference comes from:
- values.yaml file (default values)
- any custom values files specified with -f (e.g., helm install -f custom-values.yaml ...)
- any --set key=value arguments provided on the command line (e.g., helm install --set replicaCount=3 ...)

All of these sources are merged together, then made available under .Values.

Example:

{{ .Values.appName }}
{{ .Values.componentRole }}

Note that the way these labels work can be customized by the Chart author, what we showed here are just common standards.

Understanding What's Installed and How

To get more visibility into what Helm is installing or will install, you can use the helm get manifest command. The general syntax is:

helm get manifest [RELEASE_NAME] [flags]

For example, to see the manifest of our my-wordpress release, run the following command:

helm get manifest my-wordpress

The output will be a long YAML document containing all the Kubernetes resources generated by the release. To filter the output and see only the created resources from Helm's perspective, use:

helm get manifest my-wordpress | grep '^kind:'

To show exactly which Helm templates were used to create the resources, run:

helm get manifest my-wordpress | grep '^# Source:'

ℹ️ It's worth noting that helm get manifest command shows the desired state of the release after installation which may differ from the actual state in the cluster due to various factors.

To see the current state in the cluster, run:

kubectl get all -l app.kubernetes.io/instance=my-wordpress -o yaml

Since multiple sources are merged to create the final configuration, you can also use the helm get values command to see the effective values used for the release:

helm get values my-wordpress --all

You can always get the metadata of the release using the helm get metadata command:

helm get metadata my-wordpress

Install a Chart in a Specific Namespace

By default, Helm installs charts in the default namespace. However, if you want to change this behavior you have two options:

  • Using the environment variable HELM_NAMESPACE. This will set the default namespace for all Helm commands in the current shell session.
# Set the default namespace for Helm commands
export HELM_NAMESPACE=<NAMESPACE>

# Install the chart without specifying the namespace
# and it will be installed in the namespace defined in HELM_NAMESPACE
helm install [RELEASE_NAME] [CHART]
  • Using the --namespace (or -n) flag at install time.
helm install [RELEASE_NAME] [CHART] --namespace [NAMESPACE]
# or
helm install [RELEASE_NAME] [CHART] -n [NAMESPACE]

Since version 3.2, you can force the creation of a namespace if it does not exist:

helm install [RELEASE_NAME] [CHART] --create-namespace --namespace [NAMESPACE]

For example, to install the WordPress chart in a namespace called wordpress:

# Delete any existing installation to start fresh
# This is optional and won't affect our installation
# in the new namespace
helm uninstall my-wordpress --ignore-not-found
kubectl delete pvc --all --ignore-not-found

# Install in the 'wordpress' namespace
helm install \
  my-wordpress \
  bitnami/wordpress \
  --namespace wordpress \
  --create-namespace

# Verify the installation
kubectl get all -n wordpress -l app.kubernetes.io/instance=my-wordpress

# Clean up
helm uninstall my-wordpress -n wordpress
kubectl delete pvc --all -n wordpress

This command will not delete any other WordPress installations (in the same or different namespaces). Every installation is an independent release and is unique by its name and namespace. In other words, you can have:

  • Multiple releases with the same name in different namespaces.
  • Multiple releases with different names in different namespaces.
  • Multiple releases with different names in the same namespace.

What may cause a conflict is having multiple releases with the same name in the same namespace (Helm will prevent this).

Test Installation without Deploying

Sometimes, you may want to see what resources will be created during the installation or the upgrade process without actually deploying them. Helm provides the --dry-run flag for this purpose. The general syntax is:

helm install [RELEASE_NAME] [CHART] --dry-run=client

Let's try it out:

helm install \
  my-release \
  bitnami/wordpress \
  --dry-run=client

Debugging Installation

The debug mode can be enabled using two methods:

  • Using the environment variable HELM_DEBUG. This will enable debug mode for all Helm commands in the current shell session.
# Enable debug mode for Helm commands
export HELM_DEBUG=1
# or
export HELM_DEBUG=true

# Install the chart with debug information
helm install [RELEASE_NAME] [CHART]
  • Using the --debug flag at install time.
helm install [RELEASE_NAME] [CHART] --debug

It's possible to combine the --dry-run and --debug flags:

helm install [RELEASE_NAME] [CHART] --dry-run=client --debug

To apply this to our previous example:

helm install \
  my-release \
  bitnami/wordpress \
  --dry-run=client \
  --debug

Understanding Chart Versions and Upgrading Releases

As we have seen before, the general syntax for upgrading a chart is:

helm upgrade [RELEASE_NAME] [CHART] [flags]

But we can also use the upgrade --install or upgrade -i command that could upgrade an existing release or install it if it does not exist.

helm upgrade --install [RELEASE_NAME] [CHART] [flags]

Let's see an example of how to upgrade a release to a specific chart version. First, we need to find the available versions of the chart we want to install or upgrade. We can do this using the helm search repo command. The general syntax is:

# Add the Bitnami repository if not already added
helm repo add bitnami https://charts.bitnami.com/bitnami

# Find the latest versions of the WordPress chart
helm search repo bitnami/wordpress --versions

You should see an output similar to this:

NAME                    CHART VERSION   APP VERSION DESCRIPTION
bitnami/wordpress       28.0.2          6.9.0       WordPress is the world's most popular blogging ...
bitnami/wordpress       28.0.1          6.8.3       WordPress is the world's most popular blogging ...
[...]

To install the version 28.0.1 of the WordPress chart, run the following command:

# Clean any previous installation
helm uninstall my-wordpress --ignore-not-found
kubectl delete pvc --all --ignore-not-found

# Install version 28.0.1
helm install \
  my-wordpress \
  bitnami/wordpress \
  --version=28.0.1

The installation will create a new release called my-wordpress. To upgrade the release to version 28.0.2, run the following command:

helm upgrade my-wordpress bitnami/wordpress --version=28.0.2

Viewing Release

Since we installed WordPress and then upgraded it, we can view the release information using the helm list or helm ls command. The general syntax is:

helm list
# or
helm ls

If our release is in a different namespace than default, we must specify the namespace using the --namespace or -n flag:

helm list -n [NAMESPACE]
# or
helm ls -n [NAMESPACE]

Otherwise, to list all releases in all namespaces, use the --all-namespaces or -A flag:

helm ls -A

The helm ls command should show that we have at least 2 revisions of the release.

You can also use the filter flag (--filter) to filter the releases based on a regular expression pattern. For example, to list all releases in all namespaces that have the my prefix:

helm ls --filter 'my' -A

You can also use regex:

helm ls --filter '[a-z][a-z]-w[a-z]rdpress' -A

It is possible to format the output of the helm list command using the --output flag. For example, to get YAML output, run the following command:

helm ls -A -o yaml
# or
helm ls -A --output yaml

To get JSON output, use --output json:

helm ls -A -o json
# or
helm ls -A --output json

Or combine both commands with jq or yq to process the output further:

# Colorized JSON output
helm ls -A -o yaml | yq
helm ls -A -o json | jq

# Query specific fields
helm ls -A -o yaml | yq '.[].name'

ℹ️ helm ls shows the current revision number of the release, but it does not show the version of the chart used for each revision. This is why we will use the helm history command in the next section.

Viewing Release History

To see all the revisions of a release, use the helm history command. The general syntax is:

helm history [RELEASE_NAME] [flags]

To apply this to our example:

helm history my-wordpress

The history table contains the following columns:

COLUMNDESCRIPTION
REVISIONThe revision number of the release.
UPDATEDThe date and time when the revision was created.
STATUSThe status of the release (e.g., deployed (the current successful revision), superseded (not deleted, not failed but no longer the current), failed, etc.).
CHARTThe name and version of the chart used for this revision.
APP VERSIONThe version of the application deployed by this release.

Since the beginning of this section, we have been using the bitnami/wordpress chart and we made multiple upgrades. However, by default, Helm only keeps the last 10 revisions of a release even if more upgrades were made. If you want to keep more revisions, you have 2 options:

  • Using the environment variable HELM_MAX_HISTORY which is set by default to 10. You can change it to any value you want.
# Set the maximum number of revisions to keep
export HELM_MAX_HISTORY=[NUMBER_OF_REVISIONS]

# Install or upgrade the chart
helm upgrade -i [RELEASE_NAME] [CHART]

You can always export this variable in your shell profile (e.g., ~/.bashrc, ~/.zshrc, etc.) to make it permanent.

  • Using the --history-max flag during installation or upgrade:
helm upgrade -i [RELEASE_NAME] [CHART] --history-max=[NUMBER_OF_REVISIONS]

If we apply this to our WordPress example, it becomes:

echo "export HELM_MAX_HISTORY=20" >> ~/.bashrc
source ~/.bashrc

# Try upgrading again with more history
helm upgrade -i \
  my-wordpress \
  bitnami/wordpress \
  --version=28.0.2

# Check the history
helm history my-wordpress

Helm will show up to 20 revisions of the my-wordpress release. You can also decrease the number of revisions if you want and test it out:

# Set maximum history to 1
sed -i 's/HELM_MAX_HISTORY=20/HELM_MAX_HISTORY=1/' ~/.bashrc
source ~/.bashrc

# Upgrade again
helm upgrade -i \
  my-wordpress \
  bitnami/wordpress \
  --version=28.0.2

# Check the history
helm history my-wordpress

Uninstalling a Release

The general syntax to remove a release is:

helm uninstall [RELEASE_NAME] [flags]

When a release is uninstalled, the kubernetes resources associated with the release are deleted from the cluster.

For example, to uninstall the my-wordpress release, run the following command:

helm uninstall my-wordpress

# or to avoid errors if the release does not exist
helm uninstall my-wordpress --ignore-not-found

This command will delete all the Kubernetes resources created by the my-wordpress release, except resources like volumes (e.g., PersistentVolumeClaims) created by the StatefulSet (not Helm).

# Check PVCs
kubectl get pvc -l app.kubernetes.io/instance=my-wordpress

# You need to delete them manually if you want to remove them as well
kubectl delete pvc -l app.kubernetes.io/instance=my-wordpress

Also, the release history deleted by default:

helm history my-wordpress

You should see an error similar to this: Error: release: not found. If you want to keep the release history when uninstalling a release, use the --keep-history flag:

# Reinstall
helm upgrade -i my-wordpress bitnami/wordpress --version=28.0.2

# Uninstall (keep history)
helm uninstall my-wordpress --keep-history

# Check history
helm history my-wordpress

You should see the history of the release even if it has been uninstalled.

You can see all the deleted charts by running the following command (only if you used the --keep-history flag):

helm list --all-namespaces --uninstalled

Dry Run Uninstall

It is possible to perform a dry run of the uninstall process using the --dry-run flag. This is useful to see what resources would be deleted without actually deleting them. The general syntax is:

helm uninstall [RELEASE_NAME] --dry-run

We can apply this to our previous example:

# Reinstall
helm upgrade -i my-wordpress bitnami/wordpress --version=28.0.2

# Dry run uninstall
helm uninstall my-wordpress --dry-run --debug

# Verify the release still exists
helm list

# Clean up
helm uninstall my-wordpress
kubectl delete pvc -l app.kubernetes.io/instance=my-wordpress

Key Takeaways

With the knowledge gained in this blog post, you should now be comfortable installing, upgrading, and managing Helm charts in your Kubernetes cluster. The goal wasn't to cover every single Helm concept but rather to provide you with an in-depth introduction to the most commonly used features and commands.

Here are the key takeaways:

  • Helm is not just a package manager; it's also a powerful templating engine that allows you to create dynamic Kubernetes manifests.
  • Helm plugins can extend the functionality of Helm by providing additional capabilities.
  • Charts define applications. A chart is a structured set of files and templates that render Kubernetes manifests from configuration values.
  • Releases are tracked installations of charts. Each install or upgrade creates a versioned release that Helm can inspect, upgrade, roll back, or uninstall.
  • helm install, helm upgrade, and helm upgrade --install are the core workflow for managing application lifecycles.
  • Configuration is driven by values. Defaults come from values.yaml and are overridden with --set or --values, producing a final merged configuration.
  • Helm labels everything it creates. These standard labels make it easy to identify, query, and manage all resources belonging to a release.
  • Dry runs and introspection matter. Commands like --dry-run, --debug, helm get, helm ls, and helm history are essential to understand what Helm will do and what it already did.

What's Next

This introduction only scratches the surface of what Helm actually enables.
Once you move past simple installs, real questions start to appear: how Helm tracks state across upgrades, how rollbacks really work, how to design charts that survive production changes, and how Helm fits into GitOps workflows without becoming a liability.

If you want to go deeper, both guides below are built to answer those questions from a practical, operator-focused perspective.

  • Helm in Practice takes you from Helm fundamentals to production-grade usage. It focuses on how Helm behaves in real environments: chart design, release lifecycle, upgrades, rollbacks, debugging, dependencies, CRDs, security, and GitOps integration.

  • Cloud Native Microservices with Kubernetes zooms out to the platform level. It shows how microservices are designed, deployed, and operated on Kubernetes, and how tools like Helm fit into a complete cloud-native delivery workflow.

Both guides are hands-on, experience-driven, and written for people who run real systems, not toy clusters.

If you’re ready to move from knowing what Helm is to using it with confidence in production, you’ll find everything you need there.

👉 Explore the guides on FAUN.sensei() and start learning how to build systems that are predictable, repeatable, and built to last.


Let's keep in touch!

Stay updated with my latest posts and news. I share insights, updates, and exclusive content.

Unsubscribe anytime. By subscribing, you share your email with @eon01 and accept our Terms & Privacy.

Give a Pawfive to this post!


Only registered users can post comments. Please, login or signup.

Start writing about what excites you in tech — connect with developers, grow your voice, and get rewarded.

Join other developers and claim your FAUN.dev() account now!

FAUN.dev()
FAUN.dev()

FAUN.dev() is a developer-first platform built with a simple goal: help engineers stay sharp without wasting their time.

Avatar

Aymen El Amri

Founder, FAUN.dev

@eon01
Founder of FAUN.dev(), author, maker, trainer & software engineer
Developer Influence
3k

Influence

328k

Total Hits

57

Posts

Featured Course(s)
Helm in Practice
Helm in Practice

Designing, Deploying, and Operating Kubernetes Applications at Scale

Observability with Prometheus and Grafana
Observability with Prometheus and Grafana

A Complete Hands-On Guide to Operational Clarity in Cloud-Native Systems

DevSecOps in Practice
DevSecOps in Practice

A Hands-On Guide to Operationalizing DevSecOps at Scale