Helm Package Manager for Kubernetes Applications
Deploying a real application on Kubernetes means writing and managing many YAML files — Deployments, Services, ConfigMaps, Secrets, Ingresses, ServiceAccounts, and more. As the number of environments and applications grows, copy-pasting and hand-editing these files becomes error-prone and slow. Helm solves this by packaging all the YAML for an application into a single, versioned, templatable unit called a chart.
Helm is the package manager for Kubernetes, just like apt is the package manager for Ubuntu or npm is the package manager for Node.js.
The Problem Helm Solves
Imagine deploying the same application — a web API — to three environments: development, staging, and production. Each environment needs slightly different settings: different replica counts, different resource limits, different domain names, different database URLs. Without Helm, you maintain three separate copies of 8–10 YAML files. When you update the Deployment image tag, you update it in three places and hope you do not miss one.
Without Helm With Helm ──────────── ───────── dev/deployment.yaml chart/ dev/service.yaml templates/ dev/ingress.yaml deployment.yaml (template) staging/deployment.yaml service.yaml (template) staging/service.yaml ingress.yaml (template) staging/ingress.yaml values.yaml (defaults) prod/deployment.yaml values-dev.yaml (overrides) prod/service.yaml values-staging.yaml prod/ingress.yaml values-prod.yaml ... 9 files × 3 environments 1 chart + 3 small values files = 27 files to maintain = easy to maintain
Helm Core Concepts
Chart
A chart is a directory of files that describes a Kubernetes application. It contains templates (YAML files with placeholders), default values, and metadata. A chart is the distributable, versioned package.
Values
Values are the variables that fill in the placeholders in the templates. The chart ships with a values.yaml file containing sensible defaults. You override specific values per environment without touching the templates.
Release
When you install a chart into a Kubernetes cluster, Helm creates a release. A release is a specific running instance of a chart with a specific set of values. You can install the same chart multiple times (each with different values) and get multiple releases.
Repository
A Helm repository is a collection of packaged charts served over HTTP. Public repositories like Artifact Hub host charts for popular open-source software — databases, monitoring stacks, ingress controllers, and more.
┌────────────────────────────────────────────────────────────┐ │ Helm Component Relationship │ │ │ │ Repository │ │ ────────── │ │ chart A v1.0 ──► helm install ──► Release "my-app" │ │ chart A v1.1 (chart A v1.0 + │ │ chart B v2.3 values-prod.yaml) │ │ │ │ │ ▼ │ │ Kubernetes Objects │ │ (Deployment, Svc...) │ └────────────────────────────────────────────────────────────┘
Helm Chart Structure
my-app/ ├── Chart.yaml # Chart metadata (name, version, description) ├── values.yaml # Default values for templates ├── templates/ │ ├── deployment.yaml # Kubernetes Deployment template │ ├── service.yaml # Kubernetes Service template │ ├── ingress.yaml # Kubernetes Ingress template │ ├── _helpers.tpl # Reusable template snippets │ └── NOTES.txt # Post-install instructions shown to user └── charts/ # Dependency charts (sub-charts)
Chart.yaml
apiVersion: v2 name: my-app description: A web API for the my-app service type: application version: 1.3.0 # Chart version (you increment this) appVersion: "2.1.0" # Application version inside the chart
values.yaml: Default Configuration
replicaCount: 1
image:
repository: myregistry/my-app
tag: "2.1.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: false
host: "example.com"
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
Helm Templates: Writing Parameterised YAML
Templates use the Go templating language. Double curly braces {{ }} are placeholders that Helm replaces with values at render time.
Deployment Template Example
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-app.selectorLabels" . | nindent 6 }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources:
{{- toYaml .Values.resources | nindent 10 }}
When Helm renders this template with the production values file, it substitutes .Values.replicaCount with 5, .Values.image.tag with 3.0.1, and so on. The result is plain Kubernetes YAML that kubectl can apply.
Conditional Blocks
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "my-app.fullname" . }}
spec:
rules:
- host: {{ .Values.ingress.host }}
...
{{- end }}
The entire Ingress block is omitted when ingress.enabled is false. This means the development environment gets no Ingress while production does — all controlled by a single boolean in the values file.
Essential Helm Commands
Working with Repositories
# Add a repository helm repo add bitnami https://charts.bitnami.com/bitnami # List repositories helm repo list # Update cached chart index helm repo update # Search for charts helm search repo nginx helm search hub postgresql # Search Artifact Hub
Installing and Managing Releases
# Install a chart (creates a new release named "my-nginx") helm install my-nginx bitnami/nginx # Install with custom values helm install my-app ./my-app -f values-prod.yaml # Install into a specific namespace, create it if missing helm install my-app ./my-app \ --namespace production \ --create-namespace # Preview what would be installed (dry run) helm install my-app ./my-app --dry-run # See the rendered YAML without installing helm template my-app ./my-app -f values-prod.yaml
Upgrading and Rolling Back
# Upgrade a release (updates the running resources) helm upgrade my-app ./my-app -f values-prod.yaml # Upgrade; if it fails, automatically roll back helm upgrade my-app ./my-app \ --atomic \ --timeout 5m # See upgrade history helm history my-app -n production # Roll back to a previous revision helm rollback my-app 2 -n production
Helm tracks every upgrade as a numbered revision. Rolling back is a one-command operation that restores the exact state of a previous release.
Inspecting Releases
# List all releases in a namespace helm list -n production # Get the values used for a release helm get values my-app -n production # Get the full rendered manifests for a release helm get manifest my-app -n production # Uninstall a release helm uninstall my-app -n production
Values Override Hierarchy
Helm merges values from multiple sources. Later sources override earlier ones:
┌───────────────────────────────────────────────────────┐ │ Values Precedence (highest last) │ │ │ │ 1. chart/values.yaml (packaged defaults) │ │ 2. Parent chart values.yaml (if a subchart) │ │ 3. -f values-prod.yaml (environment file) │ │ 4. --set key=value (command-line override)│ │ │ │ Higher number wins when the same key is set twice. │ └───────────────────────────────────────────────────────┘
A typical workflow: the chart's values.yaml holds safe defaults, each environment has its own values-{env}.yaml file, and individual deployments pass specific overrides with --set when needed.
Chart Dependencies
A chart can declare dependencies on other charts inside Chart.yaml. For example, an application chart can declare a dependency on the Bitnami PostgreSQL chart so the database gets deployed alongside the app.
# Chart.yaml dependencies: - name: postgresql version: "13.2.0" repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled
# Download dependencies into the charts/ directory helm dependency update # Install the app + PostgreSQL together helm install my-app ./my-app \ --set postgresql.enabled=true \ --set postgresql.auth.password=supersecret
Creating a Chart from Scratch
# Generate a chart scaffold helm create my-app # The scaffold creates a working Nginx-based chart # Edit templates/ and values.yaml to match your app # Lint the chart for errors helm lint ./my-app # Package the chart into a .tgz archive for distribution helm package ./my-app # Creates: my-app-1.3.0.tgz
Helm Hooks: Lifecycle Events
Helm hooks let you run Kubernetes Jobs at specific points in the chart lifecycle. Common use cases:
- pre-install / pre-upgrade — Run database migrations before the new version of the app starts.
- post-install / post-upgrade — Send a deployment notification or run smoke tests.
- pre-delete — Back up data before the release is uninstalled.
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
spec:
containers:
- name: migrate
image: myapp:{{ .Values.image.tag }}
command: ["./migrate", "up"]
restartPolicy: Never
Helm in CI/CD Pipelines
Helm integrates naturally into CI/CD workflows. A typical pipeline step upgrades a release with the new image tag as a --set override:
# In a GitHub Actions or GitLab CI step: helm upgrade --install my-app ./chart \ --namespace production \ --set image.tag=$IMAGE_TAG \ --atomic \ --timeout 10m \ --wait
The --atomic flag rolls back automatically if the upgrade fails. The --wait flag blocks until all Pods are ready or the timeout expires. This turns a Helm upgrade into a reliable, self-recovering deployment step.
Key Points
- Helm packages all Kubernetes YAML for an application into a versioned, templatable chart.
- Charts separate the template structure from environment-specific values, eliminating YAML duplication across environments.
- A release is one installed instance of a chart. You can install the same chart multiple times with different values and get different releases.
- Use
helm upgrade --atomicin CI/CD pipelines for automatic rollback on failure. - Helm tracks every upgrade as a numbered revision — rolling back is a single
helm rollbackcommand. - Hooks let you run Jobs at lifecycle events such as pre-upgrade database migrations.
- Public charts for databases, monitoring, and infrastructure tools are available on Artifact Hub and avoid the need to write complex YAML from scratch.
