Helm Made Simple: Managing Kubernetes Apps with Charts

Sep 28, 2025 posted by Ilman Iqbal

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.

Helm Kubernetes illustration

What is Helm?

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:

Why not just kubectl apply -f?

If you only use kubectl, you'll encounter several practical problems:

Helm Concepts in a Nutshell

Installing Helm on Ubuntu

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!

Working with Helm Repositories (Using Curity as an Example)

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. The name curity in the above command is the local repo alias you choose for this chart repository — it's how you'll refer to it later (e.g. curity/idsvr when installing). You can list these aliases at any time with helm repo list.

Note: you do not select a Kubernetes namespace for this command. helm repo add only stores repo metadata locally on your machine — it does not interact with the cluster yet. A namespace is needed only when you install charts, upgrade releases, or list releases in a namespace.

2. Verify repositories

helm repo list
# Output
NAME            URL
curity          https://curityio.github.io/idsvr-helm/
ingress-nginx   https://kubernetes.github.io/ingress-nginx
kyverno         https://kyverno.github.io/kyverno/

Now Helm knows about the curity repository (along with any other repos you may have added previously, such as ingress-nginx or kyverno).

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. Upgrade to a newer chart version

Over time, the chart maintainers publish new chart versions (bug fixes, new app versions, or new features). Helm makes it easy to roll out a newer chart version while keeping the same release name.

9.1 Refresh chart repository metadata

Before upgrading, refresh your local copy of the repo so Helm knows about the latest available charts:

helm repo update curity
# Output
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "curity" chart repository
Update Complete. ⎈Happy Helming!⎈

Running helm repo update without a repo name refreshes all configured repositories at once.

9.2 Check what is currently installed

helm list -n curity
# Output
NAME            NAMESPACE   REVISION   UPDATED                       STATUS    CHART           APP VERSION
idsvr-tutorial  curity      1          2026-04-18 12:13:58 +0530     deployed  idsvr-0.13.17   10.7

The CHART column shows the chart version currently deployed (idsvr-0.13.17) and APP VERSION shows the underlying app version (10.7). The release name idsvr-tutorial is the running instance of the chart — you'll reuse it in the upgrade command.

9.3 List all available chart versions

After refreshing the repo, you can confirm that newer versions are available:

helm search repo curity
# Output
NAME            CHART VERSION   APP VERSION   DESCRIPTION
curity/idsvr    0.13.20         11.2          A Helm chart for Curity Identity Server

By default, helm search repo shows only the latest chart version. To see every published version, use --versions:

helm search repo curity/idsvr --versions
# Output
NAME            CHART VERSION   APP VERSION   DESCRIPTION
curity/idsvr    0.13.20         11.2          A Helm chart for Curity Identity Server
curity/idsvr    0.13.19         11.1          A Helm chart for Curity Identity Server
curity/idsvr    0.13.18         11            A Helm chart for Curity Identity Server
curity/idsvr    0.13.17         10.7          A Helm chart for Curity Identity Server

This lets you pick a specific target chart version rather than always jumping to the latest.

9.4 Inspect default values for a specific chart version

Each chart version may ship with different defaults (image tag, replica count, etc.). Inspect them before upgrading so you know what you're getting:

helm show values curity/idsvr --version 0.13.19
# Output (truncated)
# Default values for curity.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1
revisionHistoryLimit: 10

# Additional annotations of the ServiceAccount.
serviceAccountAnnotations: {}

image:
  repository: curity.azurecr.io/curity/idsvr
  tag: 11.1
  pullPolicy: IfNotPresent
  pullSecret:

Notice that the image tag (11.1) is set inside the chart itself — the chart already pairs the correct app image with each chart version. So you usually don't need to override image.tag during the upgrade.

9.5 Upgrade the release to the new chart version

helm upgrade idsvr-tutorial curity/idsvr \
  --version 0.13.19 \
  --namespace curity \
  --set curity.admin.logging.level=INFO \
  --set curity.runtime.logging.level=INFO \
  --set curity.config.password=12345678 \
  --set curity.config.uiEnabled=true \
  --timeout 30m

- idsvr-tutorial: the existing release (running instance of the chart) you want to upgrade.
- --version 0.13.19: pins the chart version to upgrade to. If omitted, Helm picks the latest available version.
- Notice we do not set image.tag — the chart already references the right image tag (11.1) for this chart version, so let the chart's default take effect.
- The --set flags re-apply your custom configuration; any values not passed in fall back to the chart defaults.

After the upgrade completes, running helm list -n curity again will show the new chart version (idsvr-0.13.19), the new app version (11.1), and a bumped revision number:

helm list -n curity
# Output
NAME            NAMESPACE   REVISION   UPDATED                       STATUS     CHART           APP VERSION
idsvr-tutorial  curity      2          2026-05-14 15:49:40 +0530     deployed   idsvr-0.13.19   11.1

The REVISION count increases by one with every helm upgrade (so it will grow further if you run additional config-only upgrades from step 8). You can review the history of all revisions with helm history idsvr-tutorial -n curity, and roll back to any previous revision using helm rollback idsvr-tutorial <revision> -n curity.

10. 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.

Helm Chart Structure

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.

Converting Existing Kubernetes Manifests into a Helm Chart

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:

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.

4. 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:

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.

Share your Helm chart

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.

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.