How to K8s: Getting Started with Helm Charts

Helm simplifies the process of maintaining and deploying large numbers of K8s resources by bundling them up into a single package called a Helm Chart that can be configured easily for different environments. Learn how to get started with Helm in this post.

Getting Started with Helm Charts

Kubernetes resource definitions, written in YAML, can get pretty involved pretty fast. But when you are deploying a full application to K8s, the accumulation of all of the required YAML definition files is likely to be more than a handful to manage and keep track of. Moreover, deploying a large number of resources one by one is tedious at best and downright impractical when you’re deploying to anything other than a development environment.

Thankfully, Helm – a package manager for Kubernetes – makes it relatively simple to bundle up all of your related resource definitions into a single package, or Chart in Helm speak, that can be deployed all at once. Moreover, once a Helm Chart has been developed, it can be pushed to a registry, and reused by other team members, or even the larger development community in the case of open-source projects.

Today, we’ll look walk through the process of creating a Helm Chart that we can use to generate a Helm Template, which – in turn – we can use to deploy all of our resources to Kubernetes in a single call to kubectl.

Installing Helm on Mac

You can install Helm on macOS with Homebrew, like so:

brew install helm

Creating Your First Helm Chart

helm create <chart-name>

This will create a new directory named according to the <chart-name> value you pass in the command. It will contain a variety of autogenerated files that you can edit to align with your specific project.

Specifically, you will see the following:

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

As this is our first Helm Chart, we will reduce the complexity a good bit by removing unnecessary files, but this is the base template you will first see in the new directory.

You can go ahead and remove the suggested templates (we’ll replace them with our own, working examples shortly) so that your directory contains the following:

├── Chart.yaml
├── charts
├── templates
└── values.yaml

Next, we’ll need to replace those files with some of our own – these can be any valid K8s resource definitions, such as deployments, services or jobs – any YAML file that represents a Kubernetes resource.

Paste the following into templates/deployment.yml:

apiVersion: apps/v1
kind: Deployment
name: k8s-s3-archive
app: s3archive
replicas: 1
app: s3archive
app: s3archive
- name: k8s-s3-archive
image: {{ .Values.image }}
value: {{ .Values.aws_secret_key }}
value: {{ .Values.aws_access_key }}
- name: S3_BUCKET_NAME
value: {{ .Values.s3_bucket_name }}
value: {{ .Values.aws_region_name }}
- containerPort: 8888

Notice that we have a number of values wrapped in double curly braces, i.e., {{ ... }}. This allows us to dynamically populate those values with information drawn from our values.yaml file that we will update shortly.

This is powerful, because we can now configure our deployment for any number of different environments without ever having to change this file. Instead, we can simply create a separate file for each deployment we need. For example, we might have a deployment_values.yaml file and a development_values.yaml file that each contain different information, which we can embed into this resource definition by selecting the specific set of values we want with Helm.

Next paste this snippet into templates/service.yml:

apiVersion: v1
kind: Service
name: s3archive-service
type: ClusterIP
app: s3archive
- port: 80
targetPort: 8888

And, finally, update the image in your new values.yaml file with the values below, and add the values listed below to the bottom of that same file:

# values to update
repository: jdvincent/orka-s3-archive
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "latest"

# values to add
aws_region_name: "<your-desired-region>"
aws_secret_key: "<your-aws-secret-key>"
aws_access_key: "<your-aws-access-key>"
s3_bucket_name: "<your-s3-bucket-name>"

Building a Single Template From all Included Resource Definitions

Once we have the files listed above created and updated as described, we can use the Helm CLI to achieve three things. First, we will validate the YAML in each of the included templates. If there is a syntax error, we will get an error message telling us which line we need to fix. We will also combine all of the included files into one, big file that can be passed to kubectl in a single call to deploy them all. And finally, we can populate the files with values we define in our values.yaml file.

To do so, run the following from the root of Helm Chart’s directory:

helm template . --values values.yaml > output.yaml

This command will perform each of the three actions described above, and it will write the output to our new output.yaml file.

With that done, we can then pass this new output.yaml file to kubectl like so:

kubectl apply -f output.yaml

When we run this command, it will create each of the included K8s resources in our cluster with the configuration details defined in our values.yaml file.

Deploying Our New Helm Template


To do this locally, we’ll have to install a local Kubernetes cluster. Minikube is an easy to use, open-source option, so we’ll go with that here today.

You can install Minikube on Mac with Homebrew, like so:

brew intall minikube

Then, we’ll start our local cluster by running:

minikube start

Once you run the above, you will also have the Kubernetes CLI kubectl installed and configured to work with your new, local cluster.

Once we have our local cluster running and our output.yaml file created, we can pass it to kubectl to deploy it to the cluster, like so:

kubectl apply -f output.yaml

Finally, we can check on the status of our various resources by running the following:

kubectl get deployments
kubectl get services

Note that we will need to have included valid values in our values.yaml file for everything to work as it ought to, but even if we didn’t, we can still see that we passed valid YAML in our kubectl apply call, and that even if they didn't succeed, the resources we defined were deployed to Minikube in a single call.


For full applications to be deployed to Kubernetes, there will be a significant number of individual K8s resource definitions that need to be deployed. Moreover, you will likely need to deploy your application to multiple environments throughout the development and release processes. Helm Charts simplify the process of maintaining and deploying large numbers of K8s resources by bundling them up into a single package called a Helm Chart.

Above, we walked through the process of creating a Helm Chart with our own K8s YAML resource definitions, which we populated with values that can be easily maintained and updated as needed without having to change anything other than our values.yaml file.