Skip to content

Helm and Package Management

Helm is the package manager for Kubernetes: it turns a directory of templated manifests into a versioned, parameterized Chart that gets installed as a tracked Release.

The problem: raw YAML does not parameterize or version well

Section titled “The problem: raw YAML does not parameterize or version well”

A handful of Deployment, Service, and ConfigMap YAML files works fine for one environment. It falls apart the moment there is more than one: dev, staging, and prod need different replica counts, different resource limits, different image tags, and different hostnames, but the underlying shape of the manifests is identical. Copy-pasting the YAML three times and hand-editing each copy means every future change has to be applied three times, correctly, in sync. There is also no built-in notion of “this set of 12 manifests is one versioned unit” — kubectl apply treats each file independently, with no shared history and no easy way to package the whole set for someone else to install.

Helm solves both problems at once. It packages a set of related manifests into a single unit called a Chart, replaces hardcoded values with template placeholders, and tracks every install of that Chart as a Release with its own version history.

A Chart is a directory with three key pieces:

mychart/
├── Chart.yaml # name, version, and other chart metadata
├── values.yaml # default configuration values
└── templates/
├── deployment.yaml
├── service.yaml
└── configmap.yaml

Chart.yaml holds metadata (name, version, description). values.yaml holds the default configuration. Everything under templates/ is a Kubernetes manifest written with Go template syntax, where {{ .Values.xxx }} pulls a value in from values.yaml at render time:

# templates/deployment.yaml (excerpt)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-web
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: web
image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'
values.yaml
replicaCount: 2
image:
repository: myregistry/web
tag: '1.4.0'

helm template (renders locally without installing) or helm install fills in those placeholders and produces plain Kubernetes YAML — Helm never invents a new deployment mechanism, it just generates the manifests kubectl would apply anyway.

Supplying environment-specific configuration

Section titled “Supplying environment-specific configuration”

The same Chart deploys to dev, staging, and prod by overriding values.yaml at install time, either with a dedicated override file or individual --set flags:

Terminal window
# Render the manifests without installing, to sanity-check the output
helm template myapp ./mychart -f values-prod.yaml
# Install into prod using a prod-specific values file
helm install myapp ./mychart -f values-prod.yaml
# Override a single value on the command line instead
helm install myapp ./mychart --set replicaCount=5
values-prod.yaml
replicaCount: 5
image:
tag: '1.4.0'

The Chart’s templates never change between environments — only the values fed into them do.

Every helm install creates a Release: one named, tracked instance of a Chart running in the cluster. Helm keeps a revision history for each Release, so upgrades and rollbacks operate on the whole set of resources the Chart manages, not on one Deployment at a time — conceptually the same idea as kubectl rollout history for a single Deployment, just scoped to everything a Chart installed together.

Terminal window
# Upgrade an existing Release to a new chart version or new values
helm upgrade myapp ./mychart -f values-prod.yaml
# Roll an entire Release back to a previous revision
helm rollback myapp 2
# See the revision history for a Release
helm history myapp
# Remove a Release and the resources it created
helm uninstall myapp
flowchart LR
  values["values.yaml (defaults)"] --> render["helm install / helm template"]
  overrides["values-prod.yaml or --set"] --> render
  templates["templates/ (Go-templated manifests)"] --> render
  render --> manifests["Rendered Kubernetes manifests"]
  manifests --> cluster["Applied to cluster as Release myapp, revision N"]
A Chart's values and templates render into manifests, applied to the cluster as a tracked Release
What is the difference between a Helm Chart and a Helm Release
How is environment-specific configuration typically supplied to a Chart
How do helm upgrade and helm rollback relate to kubectl rollout for a single Deployment
What does helm template do differently from helm install