Back to Scaling Containers guides

Understanding Helm Charts in Kubernetes

Ayooluwa Isaiah
Updated on April 14, 2025

When working with Kubernetes, managing application deployments can quickly become complex. As applications grow, so does the number of configuration files needed to deploy them.

Helm, often described as the package manager for Kubernetes, offers an elegant solution to this challenge through the concept of Helm charts.

In this guide, we'll dive deep into what Helm charts are, how they work, and how you can use them to simplify your Kubernetes deployments.

What are Helm charts?

Helm charts are packages of pre-configured Kubernetes resources that can be managed as a single unit. Think of them as the equivalent of a software package in traditional package managers like apt, yum, or npm, but specifically designed for Kubernetes applications.

A Helm chart contains all the resource definitions necessary to run an application in a Kubernetes cluster. This includes deployments, services, config maps, secrets, and any other Kubernetes resources your application might need. By bundling these resources together, Helm charts make it significantly easier to deploy and manage complex applications on Kubernetes.

 
mychart/
  Chart.yaml          # Contains chart metadata and information
  values.yaml         # Default configuration values
  templates/          # Directory containing templated YAML files
    deployment.yaml
    service.yaml
  charts/             # Directory for dependent charts
  LICENSE             # Optional: Contains the chart license
  README.md           # Optional: Documentation

The problem Helm charts solve

To understand why Helm charts are valuable, consider this common scenario:

You have a microservice-based application consisting of several components, each requiring multiple Kubernetes resources (deployments, services, config maps, etc.). For each environment (development, staging, production), these resources need slight variations in configuration.

Without Helm, you would:

  1. Maintain separate YAML files for each resource
  2. Manually track which files go together
  3. Create different versions of these files for each environment
  4. Manually handle updates and rollbacks

This approach becomes unwieldy as your application grows. Helm charts solve this problem by:

  1. Bundling related resources together
  2. Providing templating capabilities for configuration
  3. Managing versioning and release history
  4. Simplifying updates and rollbacks

Core concepts of Helm charts

Before diving deeper into Helm charts, let's understand some core concepts:

  • Chart: A package containing all the resource definitions needed to run an application on Kubernetes.

  • Release: An instance of a chart running in a Kubernetes cluster. When you install a chart, Helm creates a new release. Each release has a unique name.

  • Repository: A place where charts can be stored and shared. Similar to Docker Hub for Docker images.

  • Values: Configuration options for a chart that can be overridden during installation or upgrade.

Anatomy of a Helm chart

Let's look at the key components of a Helm chart:

Chart.yaml

This file contains metadata about the chart, such as its name, version, description, and dependencies.

Chart.yaml
apiVersion: v2
name: my-application
version: 1.0.0
appVersion: "1.16.0"
description: A Helm chart for my application
dependencies:
  - name: mongodb
    version: 10.0.0
    repository: https://charts.bitnami.com/bitnami

values.yaml

This file contains default configuration values for the chart. These values can be overridden during installation.

values.yaml
replicaCount: 2
image:
  repository: nginx
  tag: 1.19.0
  pullPolicy: IfNotPresent
service:
  type: ClusterIP
  port: 80
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 50m
    memory: 64Mi

Templates directory

This directory contains template files that generate Kubernetes manifests. These templates use values from the values.yaml file and can include conditional logic and loops.

templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-deployment
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: 80
        resources:
          {{- toYaml .Values.resources | nindent 12 }}

In this template, expressions like {{ .Values.replicaCount }} will be replaced with values from the values.yaml file. The .Release.Name refers to the name given when installing the chart.

Creating your first Helm chart

Let's create a simple Helm chart for a web application. Before you proceed, ensure that you have helm installed.

 
helm create my-webapp

This command generates a chart with the default structure:

my-webapp

Edit the Chart.yaml file to include your application details:

Chart.yaml
apiVersion: v2
name: my-webapp
version: 0.1.0
appVersion: "1.0.0"
description: A simple web application Helm chart

Then modify the values.yaml file with your application's configuration:

values.yaml
replicaCount: 1

image:
  repository: nginx
  tag: "latest"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 50m
    memory: 64Mi

The default templates include deployment.yaml, service.yaml, and others. You can modify these or create new ones based on your requirements.

Now you can validate your chart using the lint and template commands:

 
helm lint
Output
==> Linting my-webapp
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, 0 chart(s) failed

Helm lint

 
helm template my-webapp

This command will output the rendered Kubernetes manifests without installing them.

Installing a Helm chart

Once your chart is ready, you can install it in your Kubernetes cluster:

 
helm install my-release .

This command installs the chart with the release name my-release. You can customize the values during installation if you wish:

 
helm install my-release . --set replicaCount=2 --set service.type=NodePort

Or use a custom values file like this:

 
helm install my-release my-webapp -f custom-values.yaml

Installing Helm chart

Working with Helm repositories

Helm charts can be shared through repositories. The Artifact Hub (previously known as the Helm Hub) is a public repository for Helm charts.

For example, here's how to add the Bitnami application catalog:

 
helm repo add bitnami https://charts.bitnami.com/bitnami
Output
"bitnami" has been added to your repositories

Then update your repose as follows:

 
helm repo update

Helm update repos

Once the repos are up-to-date, you can search for Helm charts with:

 
helm search repo nginx
Output
NAME                                            CHART VERSION   APP VERSION     DESCRIPTION
bitnami/nginx                                   19.1.0          1.27.4          NGINX Open Source is a web server that can be a...
bitnami/nginx-ingress-controller                11.6.13         1.12.1          NGINX Ingress Controller is an Ingress controll...
bitnami/nginx-intel                             2.1.15          0.4.9           DEPRECATED NGINX Open Source for Intel is a lig...
prometheus-community/prometheus-nginx-exporter  1.2.0           1.4.1           A Helm chart for NGINX Prometheus Exporter

You will likely see a few results with hopefully the chart you're looking for. Once you've identified it, go ahead and install it using:

 
helm install my-nginx bitnami/nginx

Installing Helm Chart

Customizing charts with templating

One of the most powerful features of Helm is its templating engine, which is based on Go templates. This allows you to create dynamic Kubernetes manifests that adapt to different environments and configurations.

Basic templating

templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config
data:
  app.properties: |
    environment={{ .Values.environment }}
    debug={{ .Values.debug }}

Conditionals

templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-service
spec:
  {{- if eq .Values.service.type "NodePort" }}
  type: NodePort
  {{- else if eq .Values.service.type "LoadBalancer" }}
  type: LoadBalancer
  {{- else }}
  type: ClusterIP
  {{- end }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: {{ .Release.Name }}

Loops

templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config
data:
  {{- range $key, $value := .Values.configData }}
  {{ $key }}: {{ $value | quote }}
  {{- end }}

Upgrading and rolling back releases

Helm makes it easy to upgrade applications and roll back to previous versions if needed.

To upgrade to a new release, use:

 
helm upgrade my-release my-webapp --set replicaCount=3

This command updates the existing release with new values.

You can also check the revision history with:

 
helm history my-release
Output
REVISION    UPDATED                     STATUS      CHART            APP VERSION    DESCRIPTION
1           Tue Apr 13 10:00:00 2025    superseded  my-webapp-0.1.0  1.0.0          Install complete
2           Tue Apr 13 11:00:00 2025    deployed    my-webapp-0.1.0  1.0.0          Upgrade complete

Finally, you can revert to a previous version using:

 
helm rollback my-release 1

This command rolls back to revision 1.

Managing dependencies with Helm charts

Helm allows you to manage dependencies between charts, making it easier to deploy complex applications. To define these dependencies, you'll add them to your Chart.yaml file.

For example, if your application requires MongoDB, you can specify it as a dependency by including the relevant information about the chart: its name, the specific version you need, the repository where it can be found, and optionally a condition that determines whether it should be installed.

Here's how you would define a MongoDB dependency:

Chart.yaml
apiVersion: v2
name: my-application
version: 1.0.0
dependencies:
  - name: mongodb
    version: 10.0.0
    repository: https://charts.bitnami.com/bitnami
    condition: mongodb.enabled

The condition field is particularly useful as it allows you to make the dependency optional. In this example, MongoDB will only be installed if the value mongodb.enabled is set to true in your values.yaml file. This gives you flexibility to deploy your application with or without certain dependencies based on your environment requirements.

Once you've defined your dependencies, you need to download them before installing your chart. You can do this with the dependency update command:

 
helm dependency update my-application

This command reads the dependencies from your Chart.yaml file and downloads all the required charts to the charts/ directory within your own chart. Each dependency is downloaded as a packaged .tgz file, which will be extracted and installed when you deploy your main chart.

When you install your chart, Helm will automatically install all these dependencies in the correct order, handling any relationship or dependency chain that might exist between them.

This approach to managing dependencies significantly simplifies the deployment of complex applications, ensuring that all components are installed consistently and with the correct configurations.

It also makes your charts more modular and reusable, as you can easily swap in different versions of dependencies or conditionally include them based on your deployment needs.

Creating a real-world application chart

Let's create a more realistic example: a chart for a web application with a Redis cache. We'll walk through the entire process step by step.

To begin, we'll initialize a new Helm chart using the helm create command. This generates the standard directory structure and template files that we'll customize for our application:

 
helm create web-app

Next, we need to define our chart's dependencies. Since our application requires Redis, we'll add it as a dependency in the Chart.yaml file. We'll specify that we want to use version 16.0.0 of the Redis chart from the Bitnami repository, and we'll make it conditional so it can be enabled or disabled as needed:

Chart.yaml
apiVersion: v2
name: web-app
version: 0.1.0
appVersion: "1.0.0"
description: Web application with Redis cache
dependencies:
  - name: redis
    version: 16.0.0
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled

With our dependencies defined, we'll now configure the default values for our chart in the values.yaml file. We'll set up our application to run two replicas using a specific image.

We'll configure a ClusterIP service to expose it within the cluster on port 80, and set up an ingress with the NGINX ingress controller. We'll also define resource limits and requests to ensure our application gets the resources it needs.

Finally, we'll configure the Redis dependency to use a standalone architecture with password authentication:

values.yaml
replicaCount: 2

image:
  repository: myapp
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  className: "nginx"
  hosts:
    - host: myapp.local
      paths:
        - path: /
          pathType: Prefix

resources:
  limits:
    cpu: 200m
    memory: 256Mi
  requests:
    cpu: 100m
    memory: 128Mi

redis:
  enabled: true
  architecture: standalone
  auth:
    enabled: true
    password: "changeme"

Now we need to update the deployment template to connect our application to Redis. We'll modify the templates/deployment.yaml file to include environment variables that point to our Redis instance.

This deployment will use the values we defined earlier, such as the replica count, image details, and resource limits.

The environment variables REDIS_HOST and REDIS_PASSWORD will allow our application to connect to the Redis instance that will be deployed as part of our chart:

templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "web-app.fullname" . }}
  labels:
    {{- include "web-app.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "web-app.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "web-app.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          env:
            - name: REDIS_HOST
              value: {{ .Release.Name }}-redis-master
            - name: REDIS_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: {{ .Release.Name }}-redis
                  key: redis-password
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

With our chart fully configured, we're ready to install it. First, we need to update the dependencies to download the Redis chart:

 
helm dependency update web-app

This command downloads the Redis chart and stores it in the charts/ directory of our web-app chart. Finally, we can install our chart with a single command:

 
helm install my-web-app web-app

This command deploys our web application and the Redis dependency to our Kubernetes cluster as a single release named "my-web-app".

Helm handles the creation of all the necessary Kubernetes resources, including the deployment, service, ingress, and Redis components.

Our application is now deployed and configured to connect to Redis using the environment variables we defined in the deployment template.

Final thoughts

Helm charts provide a powerful way to package, configure, and deploy applications on Kubernetes. By leveraging Helm's templating capabilities, dependency management, and versioning features, you can significantly reduce the complexity of managing applications in Kubernetes environments.

Whether you're deploying a simple web service or a complex microservices architecture, Helm charts offer a standardized approach that improves consistency, reproducibility, and maintainability across your entire application lifecycle.

As you continue your Kubernetes journey, consider exploring the vast ecosystem of public Helm charts available on Artifact Hub, contributing to open-source charts, or creating your own organizational chart repository to share best practices and standardized deployments across your teams.

Author's avatar
Article by
Ayooluwa Isaiah
Ayo is a technical content manager at Better Stack. His passion is simplifying and communicating complex technical ideas effectively. His work was featured on several esteemed publications including LWN.net, Digital Ocean, and CSS-Tricks. When he's not writing or coding, he loves to travel, bike, and play tennis.
Got an article suggestion? Let us know
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Make your mark

Join the writer's program

Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.

Write for us
Writer of the month
Marin Bezhanov
Marin is a software engineer and architect with a broad range of experience working...
Build on top of Better Stack

Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.

community@betterstack.com

or submit a pull request and help us build better products for everyone.

See the full list of amazing projects on github