Quick Links

Helm is a package manager for Kubernetes workloads. Creating a Helm chart for your application simplifies reproducible deployments into a Kubernetes cluster. Users can install the whole chart with one command, instead of manually applying individual component manifests with Kubectl.

In this article, we'll show you how to get started with Helm by writing a chart for a simple application. A "chart" is conceptually similar to a "package" of Kubernetes manifests which Helm lets you manage as a single entity.

What's a Helm Chart?

Helm charts apply the familiar model of package management encountered in operating systems and programming languages to applications running in a Kubernetes cluster. A Helm chart can have dependencies and children; all these components are automatically started in your cluster when you install a top-level chart.

Charts are fully versioned using a semantic release strategy. Similarly to an npm module or OS package, you can pin charts to specific versions and track their changes over time. This simplifies rolling back application deployments to a known good release should you run into issues in your cluster.

Helm charts also incorporate templating capabilities for supplying dynamic values at installation time. You can configure new applications without directly editing their manifest files, using variables that are provided by the chart developer.

Installing Helm

Helm's distributed as a plain binary via the project's GitHub Releases page. Download the correct distribution for your system, make it executable, and move it into a location within your

        PATH
    

.

Helm's also present in the package repositories of most popular Linux distributions. You can find it in Homebrew for macOS and Chocolatey and Scoop for Windows too.

Once installation's complete, run

        helm version
    

to check everything's working:

$ helm version --template='Version: {{.Version}}'

Version: v3.8.1

Helm commands target your active Kubernetes cluster connection as used by Kubectl. Your selected Kubeconfig file and context will be used for all Helm commands. You can reference a different config file with the standard KUBECONFIG environment variable or --kubeconfig flag mechanisms.

Creating a Helm Chart

Now you can begin creating a Helm chart for your application. Use the helm create command to scaffold a new chart in your working directory:

$ helm create my-app-chart

The chart's content will be deposited inside the my-app-chart directory. Let's inspect what it contains:

$ ls my-app-chart

Chart.yaml charts templates values.yaml

There are two top-level files and two supplementary sub-directories. Here's what each resource is used for:

  • Chart.yaml - Your Helm chart's manifest defining metadata properties including its name and version.
  • values.yaml - This file stores default values for variables that you can reference in your chart. It's possible to override values that are set here using CLI flags when you install the chart.
  • templates - The templates directory contains your chart's Kubernetes object manifests. Installing the chart will apply all these manifests to your cluster. Any valid Kubernetes YAML manifest can be placed here; you can also use extra functionality, such as references to variables defined in your values.yaml file. We'll look at this capability below.
  • charts - The charts directory holds other Helm charts which this one depends on. It's used to configure complex parent-child chart relationships. We won't be covering this feature in this article so you can delete the directory if you don't need it.

Helm's default chart comes pre-configured to deploy an instance of the NGINX web server. The Kubernetes manifests in the templates directory create the various constituent components, such as a Deployment, Service, and Ingress. The application is configured by variables defined in values.yaml; here you'll find settings for the image tag, service port, and Ingress host, among others:

$ cat values.yaml

# Default values for my-app-chart.

# This is a YAML-formatted file.

# Declare variables to be passed into your templates.

replicaCount: 1

image:

repository: nginx

pullPolicy: IfNotPresent

# Overrides the image tag whose default is the chart appVersion.

tag: ""

...

Installing the Chart

To add the application to your cluster, use the helm install command:

$ helm install my-app .

NAME: foo

LAST DEPLOYED: Tue Mar 29 14:47:48 2022

NAMESPACE: default

STATUS: deployed

REVISION: 1

NOTES:

...

The first argument to the command defines the name of your release. The second argument references the directory path to load the Helm chart from. You can use . when your working directory is already set to the root of your chart.

You'll receive some terminal output that describes the installed release. The NOTES section consists of information provided by the chart.

To override values.yaml variables, provide one or more instances of the --set flag:

$ helm install my-app . --set replicaCount=3 --set image.tag=1.20

This example would deploy three replicas of a container running the nginx:1.20 image. You can check this by listing the Pods in your cluster using Kubectl:

$ kubectl get pods

NAME READY STATUS RESTARTS AGE

my-app-my-app-chart-6d6577749c-2qbhb 1/1 Running 0 61s

my-app-my-app-chart-6d6577749c-wdmgv 1/1 Running 0 44s

my-app-my-app-chart-6d6577749c-x5wp7 1/1 Running 0 40s

Upgrading, Retrieving, and Deleting Chart Releases

Next make some changes to the chart or alter a variable's value:

$ helm install my-app . --set replicaCount=5

Error: INSTALLATION FAILED: cannot re-use a name that is still in use

Repeating the install command doesn't work. To apply changes to a chart that's already deployed, use the upgrade command instead. This creates a new "release" inside the cluster.

$ helm upgrade my-app . --set replicaCount=5

Release "my-app" has been upgraded. Happy Helming!

You can list all the deployed chart releases in your cluster with helm list:

$ helm list

NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION

my-app default 2 2022-03-30 15:09:34.370758719 +0100 BST deployed my-app-chart-0.1.0 1.16.0

Each release's details includes its name, the number of times it's been deployed, its last update time, and the version numbers of the chart and the application it provides.

To remove a release and destroy its Kubernetes objects, pass its name to the uninstall command:

$ helm uninstall my-app

release "my-app" uninstalled

Writing Your Own Chart From Scratch

Now let's see how to write a basic chart from scratch. Create a new directory for your chart; we're calling ours cloudsavvy-chart. Add a Chart.yaml file inside with the following content:

apiVersion: v2

name: cloudsavvy-chart

description: An example Helm chart.

type: application

version: 0.1.0

appVersion: 1.1.0

The type field should usually be set to application. The other supported type is library. Library charts package functionality that can be included as a dependency of other charts. They don't contain any Kubernetes templates themselves.

The version field refers to the version of your chart. You should increment this each time you modify the chart's templates. appVersion indicates the version of the primary software component your chart provides. It signals to chart users what will be running in their cluster after they install the chart. As an example, if you're creating a chart that distributes WordPress, it would be appropriate to set appVersion to the WordPress version number you're providing.

Next create a values.yaml file with a few simple variables:

deploymentName: cloudsavvy

image: nginx:latest

replicas: 1

These variables will be referenced in your chart template. Add this template now as templates/deployment.yaml. Our basic chart will deploy a single Pod so it'll only have one template file. In a real-world situation, it's good practice to create individual manifest files for each of your application's components.

apiVersion: apps/v1

kind: Deployment

metadata:

name: {{ .Values.deploymentName }}-deployment

spec:

selector:

matchLabels:

app: {{ .Values.deploymentName }}

replicas: {{ .Values.replicas }}

template:

metadata:

labels:

app: {{ .Values.deploymentName }}

spec:

containers:

- name: {{ .Values.deploymentName }}

image: {{ .Values.image }}

The Deployment uses values from values.yaml to configure the image reference and replica count. The deploymentName variable is used throughout so that any future changes can be made in one place. Fields in values.yaml are referenced using the {{ .Values.FIELD_NAME }} syntax.

Now use Helm to install the chart:

$ helm install cloudsavvy-app . --set replicas=3

NAME: cloudsavvy-app

LAST DEPLOYED: Tue Mar 29 15:43:21 2022

NAMESPACE: default

STATUS: deployed

REVISION: 1

TEST SUITE: None

The --set flag overrides the default value of replicas that's set in values.yaml. After the overrides have been applied, Helm injects the resolved values into the correct places in your YAML templates. The final Kubernetes manifests are then applied to your cluster. You can now verify that three containers are running by using Kubectl to list your cluster's Pods.

$ kubectl get pods

NAME READY STATUS RESTARTS AGE

cloudsavvy-deployment-7b975bd985-5r7dc 0/1 ContainerCreating 0 15s

cloudsavvy-deployment-7b975bd985-bpbkm 0/1 ContainerCreating 0 15s

cloudsavvy-deployment-7b975bd985-jzb5q 0/1 ContainerCreating 0 15s

Summary

Helm charts let you package collections of Kubernetes manifests as complete applications that are ready to deploy. You can create templated configurations that end users can easily change before installing a release into a cluster.

In this guide, we've explained the basics of Helm's functionality and shown how you can create your own simple charts for your applications. We've barely covered the surface of what Helm can achieve though - once you've written a chart, you can push it to a repository for others to use, add other charts as dependencies, and build more advanced charts using functions, pipelines, and control flow expressions. Taking the time to learn Helm makes your Kubernetes deployments more flexible, powerful, and reusable.