Learn how Helm simplifies Kubernetes application management by packaging your deployments, services, and ingress configurations into reusable charts. This guide walks you through installing Helm on Ubuntu, creating and customizing your own Helm chart for a User Management service, working with values overrides, and finally packaging and sharing your chart for reuse across different environments.
Deploying applications into Kubernetes using raw YAML manifests can be repetitive and error-prone. Imagine having to manage dozens of deployments, services, and configs for multiple environments (dev, QA, prod). That’s where Helm, the Kubernetes package manager, comes in.
Think of Helm as apt or yum for Kubernetes.
Helm helps you:
values.yaml)kubectl apply -f?If you only use kubectl, you'll encounter several practical problems:
Chart.yaml dependencies
helm history myapp
helm rollback myapp <revision-id>
helm install myapp ./myapp -f values-dev.yaml
.deb or RPM).values.yaml).Before using Helm charts, you need to install the Helm CLI. On Ubuntu or other Debian-based systems, you can install Helm in a few simple steps.
1. Update apt package index
sudo apt-get update
2. Install prerequisite packages (curl, apt-transport-https, gnupg)
sudo apt-get install -y curl apt-transport-https gnupg
3. Add the Helm GPG key
curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
4. Add the Helm apt repository
echo "deb https://baltocdn.com/helm/stable/debian/ all main" | \
sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
5. Install Helm
sudo apt-get update
sudo apt-get install helm
6. Verify installation
helm version
# Example output
version.BuildInfo{Version:"v3.16.2", GitCommit:"...", GoVersion:"go1.22.2"}
If you see a version output, Helm is installed successfully and ready to use!
Once Helm is installed, the next step is to work with Helm repositories.
These are collections of charts hosted on web servers, similar to how
apt or yum repositories store software packages.
Instead of writing and managing all Kubernetes YAML files manually, you can use charts from these repos to quickly deploy common applications like databases, message queues, or identity servers.
Let’s walk through an example with the Curity Identity Server. Curity is an OAuth and OpenID Connect server used for authentication and API security in enterprise setups. Deploying it manually with YAML would be complex, but with Helm it’s just a few commands.
1. Add the Curity Helm repository
# Add the official Curity Helm repo
helm repo add curity https://curityio.github.io/idsvr-helm/
This registers the Curity chart repo so you can install charts from it.
2. Verify repositories
helm repo list
# Output
NAME URL
curity https://curityio.github.io/idsvr-helm/
Now Helm knows about the curity repository.
3. Search for charts inside the repo
helm search repo curity
# Output
NAME CHART VERSION APP VERSION DESCRIPTION
curity/idsvr 0.13.8 10.3.0 A Helm chart for Curity Identity Server
This shows the available chart (curity/idsvr), its version, and description.
4. Install Curity Identity Server
helm install idsvr-tutorial curity/idsvr \
--namespace curity \
--create-namespace \
--set image.tag=latest \
--set curity.config.password=12345678 \
--set curity.config.uiEnabled=true \
--timeout 10m
- idsvr-tutorial: your deployment instance name for the curity/idsvr chart.
- --namespace curity: deploy into a namespace called curity.
- --create-namespace: creates the namespace if it doesn’t exist.
- --set ...: overrides values defined in the chart’s values.yaml file (e.g.,
password, enabling UI).
- --timeout 10m: sets the maximum time Helm will wait for all resources to be ready.
If deployment takes longer than this, Helm will report a timeout error.
5. Check installed releases
helm list -n curity
# Output
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
idsvr-tutorial curity 2 2025-09-24 23:37:26 deployed idsvr-0.13.8 10.3.0
This shows the deployment instance is running, which chart version is used, and its status.
6. Inspect Kubernetes resources
kubectl get deployments -n curity
# Output
NAME READY UP-TO-DATE AVAILABLE AGE
idsvr-tutorial-admin 1/1 1 1 10m
idsvr-tutorial-runtime 1/1 1 1 10m
Helm deployed two deployments: admin and runtime. Both are ready.
kubectl get svc -n curity
# Output
NAME TYPE CLUSTER-IP PORT(S)
idsvr-tutorial-admin-svc ClusterIP 10.43.209.141 6749/TCP ...
idsvr-tutorial-runtime-svc ClusterIP 10.43.66.149 8443/TCP ...
Services expose the pods inside the cluster. These ports (e.g., 6749, 8443) can be
port-forwarded.
7. Port-forward to access locally
# Forward Admin UI service to localhost:6749
kubectl port-forward svc/idsvr-tutorial-admin-svc 6749:6749 -n curity
# Forward Runtime service to localhost:9443
kubectl port-forward svc/idsvr-tutorial-runtime-svc 9443:8443 -n curity
By forwarding the service, you automatically access all pods behind it.
You can now open http://localhost:6749 to access the admin UI, and
https://localhost:9443 for the runtime.
8. Upgrade with new configs
helm upgrade idsvr-tutorial curity/idsvr \
--namespace curity \
--set curity.admin.logging.level=DEBUG \
--set curity.runtime.logging.level=DEBUG
Updates your existing deployment instance (idsvr-tutorial) with new
configuration values, without reinstalling from scratch.
9. Uninstall the release
helm delete idsvr-tutorial -n curity
Deletes all Kubernetes resources (Deployments, Services, ConfigMaps, etc.) created by this instance. The namespace remains unless you explicitly delete it.
A Helm chart is essentially a package containing all the Kubernetes manifests and configuration needed to deploy an application. A typical chart looks like this:
springboot-helm-chart/
Chart.yaml # Metadata about the chart (name, version, description)
values.yaml # Default configuration values for this chart
charts/ # Dependencies (sub-charts)
templates/ # Templates that generate Kubernetes manifests
deployment.yaml
service.yaml
ingress.yaml
_helpers.tpl # Custom template helpers for reuse
Charts use values.yaml to define configurable parameters. You can reference these values in your
templates using the syntax:
{{ .Values.variableName }}
Example usage:
# In deployment.yaml template
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
replicas: {{ .Values.replicaCount }}
# In values.yaml
image:
repository: myapp/springboot
tag: 4.0.0
replicaCount: 2
You can override these values at install or upgrade time using --set or a custom values file
(e.g., -f values-prod.yaml), allowing you to deploy the same chart across multiple environments
with different configurations.
In the Kubernetes Made Simple and Kubernetes StatefulSets with MongoDB blog post, we created several Kubernetes manifests to deploy a User Management application with a MongoDB StatefulSet:
user-management-deployment.yamluser-management-service.yamlapp-configmap.yamlmongo-headless-svc.yamlmongo-statefulset.yaml
Instead of managing these YAMLs manually, we can package them into a Helm chart for easier deployment,
configuration, and reusability. We’ll also add a new user-management-ingress.yaml to expose
the app externally.
1. Create a new chart
helm create user-management
This creates a new directory user-management/ with a standard Helm chart structure, including
Chart.yaml, values.yaml, and templates/.
2. Review and edit Chart.yaml
The Chart.yaml file defines metadata about your chart (name, description, version, etc.).
Example:
# user-management/Chart.yaml
apiVersion: v2
name: user-management
description: A Helm chart for deploying the User Management app
type: application
version: 0.1.0 # chart version
appVersion: "1.0.0" # actual app version
You can reference these values inside your templates with .Chart.*, for example:
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/version: {{ .Chart.Version }}
app.kubernetes.io/appVersion: {{ .Chart.AppVersion }}
This ensures your manifests always carry chart metadata (useful for debugging and when sharing charts).
3. Replace template files with your manifests
Copy the manifests from the earlier blog post into the templates/ folder and rename them.
By renaming, you also stick to the standard Helm chart layout
(deployment.yaml, service.yaml, ingress.yaml), which makes it easier
for others to recognize the purpose of each file at a glance. If you ever share your chart
(for example, publish it to a repo), other developers will expect to see these common naming patterns.
templates/deployment.yaml ← user-management-deployment.yamltemplates/service.yaml ← user-management-service.yamltemplates/configmap.yaml ← app-configmap.yamltemplates/mongo-statefulset.yaml ← mongo-statefulset.yamltemplates/mongo-service.yaml ← mongo-headless-svc.yamltemplates/ingress.yaml ← (new file) user-management-ingress.yaml4. Parameterize values
Replace hardcoded values in your manifests with {{ .Values.* }} placeholders and define them
in values.yaml. For example:
# templates/deployment.yaml
replicas: {{ .Values.userManagement.replicas }}
image: "{{ .Values.userManagement.image.repository }}:{{ .Values.userManagement.image.tag }}"
env:
- name: EXTERNAL_BACKEND_URL
value: "{{ .Values.userManagement.externalBackendUrl }}"
# values.yaml
userManagement:
replicas: 2
image:
repository: user-management
tag: latest
externalBackendUrl: "http://external-backend-svc:8088"
5. Add an Ingress for user-management
Create a new file templates/ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Chart.Name }}-ingress # Uses the chart name from Chart.yaml (e.g., "user-management")
namespace: {{ .Release.Namespace }} # The namespace where the Helm release is installed (set with --namespace when you run `helm install`)
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Values.userManagement.serviceName }}
port:
number: 8082
# values.yaml
ingress:
host: user-management.local
userManagement:
serviceName: user-management-svc
6. Install your chart
helm install user-mgmt user-management/ \
--namespace user-mgmt-ns \
--create-namespace \
--timeout 10m
This installs your chart into a fresh namespace user-mgmt-ns
Helm lets you customize deployments in two ways:
--set key=value → override specific values directly in the command.
helm install user-mgmt user-management/ \
--namespace user-mgmt-ns \
--set userManagement.image.tag=v2 \
--set ingress.host=custom.local
This updates the container image tag and ingress host without editing values.yaml.
-f custom-values.yaml → supply a values file with overrides.
helm install user-mgmt user-management/ \
--namespace user-mgmt-ns \
-f custom-values.yaml
This is better for multiple changes or when working in different environments
(e.g., dev, staging, prod).
7. Verify installation
kubectl get all -n user-mgmt-ns
kubectl get ingress -n user-mgmt-ns
You now have a fully parameterized Helm chart for your User Management application.
Any changes (replica counts, image versions, or ingress hostnames) can be managed easily via
values.yaml.
Once your Helm chart is ready, you can share it with others or use it across different environments. There are multiple ways to distribute your chart effectively.
helm package user-management
# This generates a versioned .tgz file like user-management-0.1.0.tgz
The version comes from Chart.yaml. You can update version and
appVersion before packaging.
# Create an index file for your repository
helm repo index ./charts --url https://example.com/charts
Explanation: helm repo index ./charts --url https://example.com/charts
generates an index.yaml file in the ./charts folder, listing all packaged
charts. The --url parameter specifies where the charts will be hosted online. This can
be any web-accessible server, such as an HTTP server, GitHub Pages, or Azure Container Registry
(ACR).
# Upload .tgz files and index.yaml to your server (e.g., ACR or web server)
# On client machines, add the repo and update
helm repo add my-charts https://example.com/charts
helm repo update
# Install the chart from the repo, with optional namespace, timeout, or value overrides
helm install user-mgmt my-charts/user-management \
--namespace user-mgmt-ns \
--create-namespace \
--timeout 10m \
--set userManagement.image.tag=v2 \
--set ingress.host=custom.local
This method is ideal for teams, CI/CD pipelines, or public chart distribution. You can override
values with --set or supply a custom values file (-f custom-values.yaml)
during install.
Note: You can host the chart repository on various platforms, including Azure Container Registry (ACR). For example, to add a Helm repository hosted on ACR:
az acr helm repo add --name <acr-name> --resource-group <resource-group> --username <username> --password <password>
helm repo update
Replace <acr-name>, <resource-group>,
<username>, and
<password> with your ACR details.
git add user-management
git commit -m "Add user-management Helm chart"
git push origin main
Other developers can clone the repo and install directly from the folder. You can also pass namespace, timeout, and value overrides as needed:
helm install user-mgmt ./user-management \
--namespace user-mgmt-ns \
--create-namespace \
--timeout 10m \
--set userManagement.image.tag=v2 \
--set ingress.host=custom.local
This approach is simple for sharing within small teams or when you do not have a dedicated Helm repository.
By versioning your chart, maintaining standard structure, and using proper packaging, you make it easier for others to reuse, update, and deploy your application consistently. Always document any required overrides, such as namespace, image tags, or ingress hosts, so that installation is smooth for other users.