Create Helm Chart

October 09, 2023


In this article, we'll guide you through the step-by-step process of creating your own Helm Chart for Kubernetes

You can find source code in our Github repo


  1. AWS EKS Cluster: Ensure that you have an AWS Elastic Kubernetes Service (EKS) cluster up and running. If you haven't created one yet, you can refer to this article for step-by-step instructions.
  2. kubectl: Install and configure kubectl, the command-line tool for interacting with your EKS cluster. You can follow the official Kubernetes documentation on Installing kubectl for installation instructions.
  3. Helm: Install Helm, the package manager for Kubernetes. Helm simplifies deploying and managing applications on Kubernetes. You can find installation instructions for Helm on the official Helm website
  4. AWS CLI: Ensure that you have the AWS Command Line Interface (CLI) installed and configured with the necessary IAM permissions. This is required for managing AWS resources related to your EKS cluster. Install the AWS CLI by following the official AWS documentation

Create Helm Chart

Create a Helm Chart:

When all our prerequisites are met, we can go to our project folder and run:

helm create mychart

This command will create a directory with the following content:

Charts directory – is where you can place sub-charts that your main Helm chart depends on. Sub-charts are essentially reusable Helm charts that can be included and managed within your primary Helm chart. Empty by default.

Templates directory – is a crucial part of a Helm chart, containing Kubernetes YAML template files. These templates define how Kubernetes resources (such as pods, services, config maps, and more) should be created within the cluster when the Helm chart is installed or upgraded

Chart.yaml file – serves as the metadata and configuration file for the Helm chart. It provides essential information about the chart, such as its name, version, description, and maintainers.

values.yaml file – contains default configuration values for your Helm chart. These values can be overridden by users during installation or upgrade, allowing for customization without modifying the Helm chart itself

Apply our chart

We can install our chart from scratch, run:

helm install mychart ./mychart

You will see similar output:

NAME: mychart
LAST DEPLOYED: Wed Oct 11 13:06:49 2023
NAMESPACE: default
STATUS: deployed
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "," -o jsonpath="{.items[0]}")
  export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT

To check if it is deployed to cluster run:

helm status mychart

You see same output as before, meaning that your chart is installed

Editing default Helm Chart:

Now we would create a new chart which we would configure for our needs:

helm create mywebapp

Edit the mywebapp/Chart.yaml file to define metadata for your chart:

apiVersion: v2
appVersion: 0.1.0
description: A Helm chart installing Demo App onto Kubernetes cluster 
name: apps
type: application
version: 0.1.0
Create Kubernetes Resources

Edit the mywebapp/templates directory to define the Kubernetes resources for your web application. For simplicity, we'll create a Deployment and a Service.

Navite to mywebapp/templates/deployment.yaml and replace default data with your own:

apiVersion: apps/v1
kind: Deployment
  namespace: {{ .Values.service.namespace }}
  name: {{ }}
    app: {{ }}
  replicas: 2
      app: {{ }}
        app: {{ }}
      - name: nginx
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        imagePullPolicy: {{.Values.image.imagePullPolicy}}
        - containerPort: {{ .Values.service.targetPort}}
            memory: {{ .Values.service.resources.memoryMiB }}Mi
            cpu: {{ .Values.service.resources.cpuMilli }}m
            memory: {{ .Values.service.resources.memoryMiB }}Mi
            cpu: {{ .Values.service.resources.cpuMilli }}m

In mywebapp/templates/service.yaml do the same:

apiVersion: v1
kind: Service
  name: {{ }}
  namespace: {{ .Values.service.namespace }}
    app: {{ }}
    service: {{ }}
    app: {{ }}
    - name: http
      port: {{ .Values.service.svcPort}}
      targetPort: {{ .Values.service.targetPort}}
  type: ClusterIP

These templates define a Deployment with two replicas and a Service for your web application using an NGINX image. As you can see we didn't hardcode some values. For example expression {{ }} is used to access a specific value from the values.yaml file. In this case, it's accessing a value nested under the service section of the values.yaml file

Customize Values

Now let's define our values in the mywebapp/values.yaml file. Leave defaults, just replace image and service section with this:

  repository: nginx
  tag: latest
  imagePullPolicy : Always

  name: my-app
  namespace: default
  svcPort: 80
  targetPort: 80
      memoryMiB: 100
      cpuMilli: 200
Customizing Notes.txt

You may noticed while installing default Helm Chart that we had some otputs.

The NOTES.txt file in a Helm chart serves as a post-installation message or guide that is displayed after a Helm release has been successfully installed or upgraded. It typically contains helpful information and instructions for users and administrators regarding the release that was just deployed.

Let's customize it. Open mywebapp/templates/NOTES.txt and paste following content:

Thank you for installing {{ }}.

Your release is named {{ .Release.Name }}.

To learn more about the release, try:

  $ helm status {{ .Release.Name }}
  $ helm get all {{ .Release.Name }}

Port forward the service to localhost port : 

kubectl port-forward service/{{ }} 20043:{{ .Values.service.svcPort}} -n {{ .Values.service.namespace }}

We are using kubectl port-forward service/{{ }} 20043:{{ .Values.service.svcPort}} -n {{ .Values.service.namespace }} to create a network proxy that listens on our local port 20043 and forwards any incoming traffic to the specified Kubernetes service. The service name, port, and namespace are determined by the values in the Helm chart's values.yaml file. This allows us to access a service running in a Kubernetes cluster as if it were running locally on your machine. We would use it to access our app later.

Install the Chart

Now we can install it to our connected cluster:

helm install mywebapp ./

Now let's check other command, which is very usefull as it not only installing chart but also upgrade it if any changes made, so you would not need to uninstall it every time you do some updates:

helm upgrade mywebapp ./

You should see the following output:

Release "mywebapp" has been upgraded. Happy Helming!
NAME: mywebapp
LAST DEPLOYED: Thu Oct 12 13:47:59 2023
NAMESPACE: default
STATUS: deployed
Thank you for installing my-app.

Your release is named mywebapp.

To learn more about the release, try:

  $ helm status mywebapp
  $ helm get all mywebapp

Port forward the service to localhost port : 

kubectl port-forward service/my-app 20043:80 -n default

Now we can check if everything is working as expected. Run:

kubectl get services -A

Also you can check if you release is up and running, as it mentioned in the output when installed or upgraded, with this cammands:

helm get all mywebapp -A

You should see your service is up and running.

Now copy generated command from the output of and run it in your terminal:

helm status mywebapp 
kubectl port-forward service/my-app 20043:80 -n default

Open your browser and navigate to localhost:20043, you should see Nginx Welcome page from your cluster

You can find source code in our Github repo