Getting Started with Using Helm to Deploy Apps on Kubernetes

Tyler Britten

Helm is a tool to help you define, install, and upgrade applications running on Kubernetes. For more information, be sure to check out Helm: What Is It?

In this guide you’ll deploy a simple application using Helm to a Kubernetes cluster.

Before You Begin

There are a few things you need to do before getting started with Helm:

Helm leverages your local Kubernetes context to operate, so it will have whatever permissions the account you’re using for kubectl does.

If you read about Helm and come across references to tiller, previous versions (before version 3) required an extra component installed on the Kubernetes cluster.

Initial Helm Setup

You’re going to need a chart to deploy with Helm, so the easiest thing is to connect to a chart repository with the helm repo command.

$ helm repo add bitnami
"bitnami" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository

Now that you have a repo connected, you need to see which charts you have available to deploy.

$ helm search repo bitnami
NAME                            	CHART VERSION	APP VERSION            	DESCRIPTION
bitnami/bitnami-common          	0.0.8        	0.0.8                  	Chart with custom templates used in Bitnami cha...
bitnami/airflow                 	5.0.3        	1.10.9                 	Apache Airflow is a platform to programmaticall...
bitnami/apache                  	7.3.9        	2.4.41                 	Chart for Apache HTTP Server
---- Truncated ----

You can see a whole list of charts, but the output above shows the first three. It shows the name of the chart, the versions, and the descriptions. As you’ll see, there’s both a chart version and an app version. That’s because a chart may be updated and changed separately from the underlying application it is deploying.

Time to Deploy a Chart (Create a Release)

Now that you have Helm configured with a repo, you can deploy a chart. In Helm lingo that’s called creating a release. In this example, you’ll deploy a pretty simple one, like nginx. You can supply a name for your app like you’re going to do here (my app) or you can use the --generate-name CLI option to have Helm generate one for you.

$ helm install my-app bitnami/nginx
NAME: my-app
LAST DEPLOYED: Mon Mar 9 07:37:28 2020
NAMESPACE: default
STATUS: deployed
Get the NGINX URL:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w my-app-nginx'

  export SERVICE_IP=$(kubectl get svc --namespace default my-app-nginx --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
  echo "NGINX URL: http://$SERVICE_IP/"

After your release is successfully created, you’ll see an output like this with the name, namespace, status, etc. The NOTES section has specific information about your install; that’s because it’s generated by Helm using a template, too.

You can see what was deployed by using kubectl.

$ kubectl get all
NAME                                READY   STATUS    RESTARTS   AGE
pod/my-app-nginx-655b5cfc8c-mfhcb   1/1     Running   0          2m38s

NAME                   TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)                      AGE
service/my-app-nginx   LoadBalancer    104.197.x.x   80:30291/TCP,443:31827/TCP   2m38s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-app-nginx   1/1     1            1           2m38s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/my-app-nginx-655b5cfc8c   1         1         1       2m38s

You can see the external IP of your application listed, but if you follow the instructions in the notes, you should see the same as well.

$ export SERVICE_IP=$(kubectl get svc --namespace default my-app-nginx --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")

$ echo "NGINX URL: http://$SERVICE_IP/"
NGINX URL: http://104.197.x.x/

$ curl $SERVICE_IP
<!DOCTYPE html>
<title>Welcome to nginx!</title>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;

You can see which releases are deployed using helm list.

my-app	default     	1       	2020-03-09 08:07:53.54657 -0400 EDT	deployed	nginx-5.1.9	1.16.1

It shows all the relevant information. Anytime you update a release, the revision number will increment.

You can clean up by removing the app with uninstall.

$ helm uninstall my-app
release "my-app" uninstalled

Changing the Values

Now you have a working nginx app, but maybe you don’t want it exposed externally via a load balancer. You can delete this app and redeploy it with ClusterIP instead of LoadBalancer.

Helm charts have a set of default values; the ones for this chart can be seen in its GitHub repository. If you look there, you’ll see the value you want to change is service.type. You can now install that same chart using the --set flag to configure it.

$ helm install my-app bitnami/nginx --set service.type=ClusterIP
NAME: my-app
LAST DEPLOYED: Mon Mar 9 08:07:53 2020
NAMESPACE: default
STATUS: deployed
Get the NGINX URL:

  echo "NGINX URL:"
  kubectl port-forward --namespace blog svc/my-app-nginx 8080:80

Notice how the NOTES section changed? It’s a template, too. You can see here that changing the service type changed the output.

In this instance you supplied the value via the CLI, but you could have also put it into a values.yaml file and used the --values CLI option. This is a common practice for when you want to supply numerous values to the chart, and/or you want to keep track of what you’re deploying by checking the file into a version control system. The easiest way to get started with your values file is to download the default one from the chart repository, like this one for the nginx chart you deployed. Any value you aren’t changing can be deleted from the file as it will be supplied by the default values. If you wanted to do that to get the same results as above you’d create a my-app-values.yaml file with these contents:

## NGINX Service properties
  ## Service type
  type: ClusterIP

The command to create the release would then be:

$ helm install my-app bitnami/nginx --values my-app-values.yaml

Upgrading a Release

Anytime you want to change anything about a release—be it a configuration value for the chart, an upgrade to the chart itself, or the application version—you’ll run helm upgrade.

For your nginx chart, you can try this by changing a configuration value. Currently the default image pullPolicy for this chart is IfNotPresent. You can change that to Always via an upgrade.

$ helm upgrade my-app bitnami/nginx --set service.type=ClusterIP,image.pullPolicy=Always

Release "my-app" has been upgraded. Happy Helming!
NAME: my-app
LAST DEPLOYED: Wed Mar 11 13:50:05 2020
STATUS: deployed
Get the NGINX URL:

  echo "NGINX URL:"
  kubectl port-forward --namespace blog svc/my-app-nginx 8080:80

You can see the revision has been incremented. If you get the nginx pod you can see the change of configuration:

$ kubectl get pod my-app-nginx-5bd7878597-pc8jp -o yaml

  - image:
    imagePullPolicy: Always

Why did you have to supply both service.type and image.pullPolicy? Because if you hadn’t supplied both, the service type would have tried to revert to the default.


What happens if you didn’t want that change or it didn’t work the way you expected? Remember the revision of the releases? You can rollback to a previous revision with helm rollback. If you want, you can do a --dry-run first to see if the rollback would even work.

$ helm rollback my-app 1 --dry-run
Rollback was a success! Happy Helming!
$ helm rollback my-app 1
Rollback was a success! Happy Helming!

If you check the pod again, you’ll see pullPolicy is set back to IfNotPreset.

Get Helming

If you’re ready to start trying to deploy more charts, there are a whole bunch of charts available in a number of different repositories. A current list of repositories in a Helm install might look like this:


Happy Helming!