Helm: The Kubernetes Package Manager

Managing Kubernetes manifests for a single microservice is manageable. But what happens when you have 50 services, across 3 environments (Dev, Staging, Prod), each needing slightly different configurations?

You end up with YAML Sprawl: hundreds of duplicate files where only the imageTag or replicaCount differs.

[!IMPORTANT] Helm solves this by treating Kubernetes applications as Packages (Charts). It combines a single Template with multiple Values files to generate the final YAML.

1. The “Why”: Solving YAML Sprawl

Without Helm, your repository looks like this:

/manifests
  /dev
  deployment.yaml  (replicas: 1)
  service.yaml
  /staging
  deployment.yaml  (replicas: 2)
  service.yaml
  /prod
  deployment.yaml  (replicas: 10)
  service.yaml

If you need to add a new environment variable, you must edit three files. This is error-prone and unscalable.

With Helm, you have one source of truth:

/my-chart
  /templates
  deployment.yaml  (replicas: {{ .Values.replicaCount }})
  values-dev.yaml    (replicaCount: 1)
  values-staging.yaml(replicaCount: 2)
  values-prod.yaml   (replicaCount: 10)

2. Architecture: Charts, Templates, and Values

Helm has three core concepts:

  1. Chart: The package structure containing all resource definitions.
  2. Values: The configuration data (JSON/YAML) that is injected into templates.
  3. Release: An instance of a chart running in a cluster. You can install the same chart multiple times as different releases (e.g., mysql-prod and mysql-dev).

The Directory Structure

A standard Helm chart looks like this:

my-chart/
  Chart.yaml          # Metadata (name, version, appVersion)
  values.yaml         # Default configuration values
  charts/             # Dependency charts
  templates/          # The template files
  deployment.yaml
  service.yaml
  _helpers.tpl      # Reusable template snippets

3. Interactive: The Template Renderer

Visualize how Helm merges values.yaml with templates/deployment.yaml to generate the final Manifest.

values.yaml

templates/deployment.yaml

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

Rendered Manifest

(Click render to generate manifest...)

4. Under the Hood: Tillerless Architecture (v3)

In Helm v2, a server-side component called Tiller ran inside the cluster with full cluster-admin privileges. This was a major security risk.

Helm v3 removed Tiller entirely.

  • Client-Only: The Helm CLI talks directly to the Kubernetes API server using the user’s local kubeconfig credentials.
  • State Storage: Release information (Secrets, ConfigMaps) is stored directly in the namespace where the application runs.
  • RBAC: Operations are constrained by the user’s RBAC roles.

5. Essential Commands

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

# Install a chart (creates a Release named 'my-redis')
helm install my-redis bitnami/redis

# Upgrade with new values
helm upgrade my-redis bitnami/redis --set architecture=replication

# Dry run (see what would happen without applying)
helm install my-nginx ./nginx-chart --dry-run --debug

6. Advanced: Helm Hooks

Hooks allow you to intervene at certain points in a release lifecycle.

  • pre-install: Run a Job to back up a database before installing.
  • post-install: Send a Slack notification after a successful deploy.
  • pre-delete: Clean up external resources before deleting the release.

Example annotation in a Job:

apiVersion: batch/v1
kind: Job
metadata:
  name: db-backup
  annotations:
  "helm.sh/hook": pre-upgrade
  "helm.sh/hook-weight": "-5"
spec:
  ...

Helm waits for the hook to complete successfully before proceeding with the upgrade.

7. Summary

  • Charts are packages of pre-configured Kubernetes resources.
  • Values allow you to customize charts for different environments without code duplication.
  • Helm v3 is secure by design (no Tiller) and uses standard Kubernetes Secrets for state.
  • Use Hooks for complex lifecycle management like database migrations.