What is Helm? Kubernetes Package Manager Explained

Raw Kubernetes YAML is powerful but repetitive. A typical application deployment involves a Deployment manifest, a Service, an Ingress, a ConfigMap, one or more Secrets, and possibly a HorizontalPodAutoscaler and PodDisruptionBudget. Copy-pasting and manually editing those files for every environment -- development, staging, production -- is a maintenance burden and a source of drift.

Helm solves this by treating a set of Kubernetes manifests as a single installable, configurable, versioned package called a chart. Helm is the officially recommended package manager for Kubernetes and is a CNCF graduated project.

How Helm Works

A Helm chart is a directory with a fixed structure:

mychart/
  Chart.yaml          # Chart metadata (name, version, appVersion)
  values.yaml         # Default configuration values
  templates/          # Kubernetes manifest templates
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl      # Reusable template snippets
  charts/             # Dependency charts (subcharts)

The templates use Go's text/template syntax to inject values. Here is a simplified deployment template:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

The values.yaml sets defaults:

replicaCount: 2

image:
  repository: mycompany/api-server
  tag: "v1.4.2"

service:
  port: 3000

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"
    memory: "512Mi"

To deploy with the defaults: helm install api-server ./mychart. To override for production: helm install api-server ./mychart -f production-values.yaml where the override file only contains the fields that differ.

Core Helm Commands

# Add the Bitnami chart repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Search for a chart
helm search repo bitnami/postgresql

# Install a chart with custom values
helm install my-postgres bitnami/postgresql \
  --namespace databases \
  --create-namespace \
  --set auth.postgresPassword=supersecret \
  --set primary.persistence.size=50Gi

# List installed releases
helm list -A

# Upgrade an existing release
helm upgrade my-postgres bitnami/postgresql \
  --reuse-values \
  --set primary.persistence.size=100Gi

# Roll back to a previous release revision
helm rollback my-postgres 1

# Uninstall a release
helm uninstall my-postgres -n databases

The rollback capability is one of Helm's most valuable features. Every helm upgrade creates a new release revision. Rolling back to the previous working version is a single command that Helm executes against the stored manifest history.

Helm in a GitOps Workflow

Helm and GitOps work together naturally. ArgoCD has native Helm support -- you point an ArgoCD Application at a chart (either a local path or a chart repository), provide a values file, and ArgoCD handles rendering and syncing. This means your production-values.yaml lives in Git, goes through code review, and any change to it triggers a sync.

A typical pattern in a GitOps repository:

# argocd-application.yaml
spec:
  source:
    repoURL: https://charts.bitnami.com/bitnami
    chart: postgresql
    targetRevision: 13.2.24
    helm:
      valueFiles:
        - values/postgresql-production.yaml

Pinning targetRevision to an exact chart version is important -- helm repo update can otherwise silently pull a new chart version with breaking changes.

Helm vs. Kustomize

The comparison comes up frequently. Helm excels when you need rich parameterization -- conditional blocks, loops over lists of values, cross-environment configuration from a single template. The Go templating can become hard to read for complex charts, but the abstraction is powerful.

Kustomize (built into kubectl since v1.14) takes a different approach: base manifests plus environment-specific overlays. There is no templating -- just patches. It is simpler to reason about and avoids the "templating hell" that poorly written Helm charts can produce.

In practice, many organizations use Helm for third-party dependencies (databases, monitoring stacks, ingress controllers) and Kustomize for their own application manifests. ArgoCD supports both.

For more on the broader Kubernetes ecosystem, see the Kubernetes guide for beginners and the DevOps tools guide.

Frequently Asked Questions