Getting Started With Helm 3

Helm is the package manager for Kubernetes — the same idea as apt on Ubuntu or npm in Node.js, but for deploying applications to your cluster. Instead of writing and maintaining dozens of raw Kubernetes YAML files per application, you define a chart once, parameterise it with values, and deploy it consistently across every environment.

Note: Helm 4 was released in 2025 with breaking changes. This guide covers Helm 3, which remains widely used and supported.


What Helm Solves

Without Helm, deploying an application to Kubernetes means manually managing a Deployment, a Service, a ConfigMap, an Ingress, HPA rules, and more — often duplicated across dev, staging, and production with small differences. Any change means updating multiple files manually.

Helm wraps all of that into a single versioned package:

flowchart LR
    Chart[Helm Chart\nTemplates + Values] --> Render[Template Engine\nGo templates]
    Render --> K8s[Kubernetes Manifests\nDeployment Service Ingress etc]
    K8s --> Cluster[Kubernetes Cluster]
    Values[values.yaml\nEnvironment overrides] --> Render

Key benefits:

  • Repeatability — same chart, same result every time
  • Versioning — upgrade and rollback with a single command
  • Templating — one chart, multiple environments via values override
  • Sharing — push to a chart repository, share with your team
  • Lifecycle management — install, upgrade, rollback, uninstall all tracked

Installing Helm

macOS:

brew install helm

Linux:

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

Windows (Chocolatey):

choco install kubernetes-helm

Verify:

helm version

Core Concepts

TermWhat it means
ChartA package of Kubernetes resource templates + default values
ReleaseA running instance of a chart in your cluster
RepositoryA server hosting a collection of charts
ValuesConfiguration that overrides chart defaults at install time
RevisionA numbered snapshot of a release — every upgrade creates a new revision

Working with Chart Repositories

Helm Artifact Hub (artifacthub.io) is the central index for public charts.

# Add a repository
helm repo add bitnami https://charts.bitnami.com/bitnami

# Update local cache
helm repo update

# Search for a chart
helm search repo bitnami/postgresql

# List all configured repos
helm repo list

Install a chart from a repository:

# Install with auto-generated name
helm install bitnami/nginx --generate-name

# Install with a specific release name
helm install my-nginx bitnami/nginx

# Install with value overrides
helm install my-nginx bitnami/nginx \
  --set service.type=LoadBalancer \
  --set replicaCount=2

Creating Your Own Chart

helm create my-app

This generates the following structure:

my-app/
├── Chart.yaml          # Chart metadata: name, version, description
├── values.yaml         # Default configuration values
├── templates/          # Kubernetes resource templates
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl    # Reusable template snippets
│   └── NOTES.txt       # Post-install notes shown to the user
└── charts/             # Sub-chart dependencies

Chart.yaml

apiVersion: v2
name: my-app
description: A Helm chart for my application
type: application
version: 0.1.0        # Chart version — increment on chart changes
appVersion: "1.0.0"   # Application version being packaged

values.yaml

replicaCount: 2

image:
  repository: my-registry/my-app
  tag: "latest"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

resources:
  limits:
    cpu: 500m
    memory: 128Mi
  requests:
    cpu: 250m
    memory: 64Mi

ingress:
  enabled: false
  host: my-app.example.com

Deployment template

Values are injected using Go template syntax with {{ }} directives:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-app.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}

The Release Lifecycle

sequenceDiagram
    participant Dev as Developer
    participant Helm as Helm CLI
    participant K8s as Kubernetes

    Dev->>Helm: helm install my-app ./my-app
    Helm->>K8s: Apply rendered manifests
    K8s-->>Helm: Release v1 created
    Helm-->>Dev: Installed - revision 1

    Dev->>Helm: helm upgrade my-app ./my-app --set image.tag=v2
    Helm->>K8s: Apply updated manifests
    K8s-->>Helm: Rolling update complete
    Helm-->>Dev: Upgraded - revision 2

    Dev->>Helm: helm rollback my-app 1
    Helm->>K8s: Restore revision 1 manifests
    Helm-->>Dev: Rolled back to revision 1
# Install
helm install my-release ./my-app

# Upgrade (with value override for production)
helm upgrade my-release ./my-app -f values.prod.yaml

# Preview what would change (dry run)
helm upgrade my-release ./my-app --dry-run --debug

# Check release status
helm status my-release

# View history
helm history my-release

# Rollback to previous revision
helm rollback my-release 1

# Uninstall
helm uninstall my-release

Environment-Specific Values

The clean pattern for multi-environment deployments is a base values.yaml with environment-specific override files:

my-app/
├── values.yaml          # base defaults
├── values.dev.yaml      # dev overrides
├── values.staging.yaml  # staging overrides
└── values.prod.yaml     # production overrides

values.prod.yaml:

replicaCount: 5
image:
  tag: "1.4.2"
resources:
  limits:
    cpu: 2000m
    memory: 1Gi
ingress:
  enabled: true
  host: app.example.com

Deploy to production:

helm upgrade --install my-app ./my-app -f values.prod.yaml

The --install flag makes upgrade behave like install if the release does not exist yet — useful in CI/CD pipelines where you do not know whether this is a first deploy or an update.


Essential Commands Reference

CommandWhat it does
helm install <name> <chart>Install a chart as a new release
helm upgrade <name> <chart>Upgrade an existing release
helm upgrade --install <name> <chart>Install or upgrade (CI-safe)
helm rollback <name> <revision>Roll back to a previous revision
helm uninstall <name>Remove a release
helm listList all releases
helm status <name>Show release status
helm history <name>Show release history
helm template <name> <chart>Render templates without installing
helm lint <chart>Validate a chart for errors
helm package <chart>Package chart into a .tgz file
helm get values <name>Show values used for a release

Demo Project

A working Helm chart is available on GitHub: devops-monk/helm-demo-project

Clone it and follow along with the commands above to see Helm in action against a real chart.

Abhay

Abhay Pratap Singh

DevOps Engineer passionate about automation, cloud infrastructure, and self-hosted tools. I write about Kubernetes, Terraform, DNS, and everything in between.