Helm and Package Management
The idea in one sentence
Section titled “The idea in one sentence”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.
Anatomy of a Chart
Section titled “Anatomy of a Chart”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.yamlChart.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/v1kind: Deploymentmetadata: name: {{ .Release.Name }}-webspec: replicas: {{ .Values.replicaCount }} template: spec: containers: - name: web image: '{{ .Values.image.repository }}:{{ .Values.image.tag }}'replicaCount: 2image: 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:
# Render the manifests without installing, to sanity-check the outputhelm template myapp ./mychart -f values-prod.yaml
# Install into prod using a prod-specific values filehelm install myapp ./mychart -f values-prod.yaml
# Override a single value on the command line insteadhelm install myapp ./mychart --set replicaCount=5replicaCount: 5image: tag: '1.4.0'The Chart’s templates never change between environments — only the values fed into them do.
Releases and lifecycle commands
Section titled “Releases and lifecycle commands”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.
# Upgrade an existing Release to a new chart version or new valueshelm upgrade myapp ./mychart -f values-prod.yaml
# Roll an entire Release back to a previous revisionhelm rollback myapp 2
# See the revision history for a Releasehelm history myapp
# Remove a Release and the resources it createdhelm uninstall myappflowchart 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"]