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:
- Maintain separate YAML files for each resource
- Manually track which files go together
- Create different versions of these files for each environment
- Manually handle updates and rollbacks
This approach becomes unwieldy as your application grows. Helm charts solve this problem by:
- Bundling related resources together
- Providing templating capabilities for configuration
- Managing versioning and release history
- 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.
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.
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.
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:
Edit the Chart.yaml
file to include your application details:
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:
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
==> Linting my-webapp
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
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
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
"bitnami" has been added to your repositories
Then update your repose as follows:
helm repo update
Once the repos are up-to-date, you can search for Helm charts with:
helm search repo nginx
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
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
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-config
data:
app.properties: |
environment={{ .Values.environment }}
debug={{ .Values.debug }}
Conditionals
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
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
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:
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:
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:
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:
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.
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
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.comor submit a pull request and help us build better products for everyone.
See the full list of amazing projects on github