Helm vs Kustomize

Key Differences in Kubernetes

Helm is a package manager that uses Go templates to generate Kubernetes manifests from parameterized charts with values files. Kustomize is a template-free tool that patches and overlays plain Kubernetes YAML files to customize them for different environments. Helm excels at packaging and distributing reusable applications; Kustomize excels at customizing existing manifests without templating.

Side-by-Side Comparison

DimensionHelmKustomize
ApproachTemplating — Go templates with {{ .Values.x }} placeholdersPatching — overlays and patches applied to base YAML files
PackagingCharts are packaged, versioned, and distributed via repositoriesNo packaging — works with plain YAML files in directories
ReusabilityCharts are designed to be shared and reused across teamsBases can be referenced but are not packaged for distribution
ComplexityMore complex — template syntax, helpers, hooks, dependenciesSimpler — standard YAML with strategic merge patches
Built-in to kubectlRequires separate Helm CLI installationBuilt into kubectl via kubectl apply -k
Release ManagementTracks releases, supports rollback via helm rollbackNo release tracking — relies on kubectl and GitOps
DependenciesSupports chart dependencies (subcharts)No dependency management — compose manually
Learning CurveSteeper — Go template syntax, chart structure, Helm conceptsGentler — plain YAML with kustomization.yaml

Detailed Breakdown

Helm — Templating Approach

Helm uses a chart structure with Go templates:

my-app/
  Chart.yaml
  values.yaml
  templates/
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl

The template files contain Go template directives:

# templates/deployment.yaml
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:
    metadata:
      labels:
        {{- include "my-app.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.containerPort }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
# values.yaml
replicaCount: 2
image:
  repository: my-app
  tag: "1.0.0"
containerPort: 8080
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

You install with:

# Default values
helm install my-release ./my-app

# Override for production
helm install my-release ./my-app -f values-prod.yaml --set replicaCount=5

Kustomize — Patching Approach

Kustomize works with plain YAML files organized in base and overlay directories:

my-app/
  base/
    kustomization.yaml
    deployment.yaml
    service.yaml
  overlays/
    dev/
      kustomization.yaml
      replica-patch.yaml
    prod/
      kustomization.yaml
      replica-patch.yaml
      resources-patch.yaml

The base contains standard, unmodified Kubernetes YAML:

# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:1.0.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml

Overlays patch the base for each environment:

# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
namePrefix: prod-
namespace: production
patches:
  - path: replica-patch.yaml
  - path: resources-patch.yaml
# overlays/prod/replica-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 5
# overlays/prod/resources-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
        - name: my-app
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 1Gi

Apply with:

# Preview the output
kubectl kustomize overlays/prod

# Apply directly
kubectl apply -k overlays/prod

Key Differences in Practice

Readability: Kustomize bases are valid Kubernetes YAML that you can kubectl apply directly. Helm templates are not valid YAML until rendered — the {{ }} syntax makes them harder to read and validate.

Debugging: With Kustomize, you can read the base file and mentally apply the patches. With Helm, you run helm template to see the rendered output, which can be surprising when template logic is complex.

Ecosystem: Helm has a massive chart ecosystem. Need PostgreSQL? helm install postgresql bitnami/postgresql. Need Redis? helm install redis bitnami/redis. Kustomize has no equivalent repository — you reference upstream YAML files or copy them.

Release Management

Helm tracks every installation as a release:

# List releases
helm list

# View release history
helm history my-release

# Rollback to a previous revision
helm rollback my-release 2

# Upgrade with new values
helm upgrade my-release ./my-app -f values-prod.yaml

Kustomize has no concept of releases. It generates YAML and applies it — the state tracking is in the Kubernetes cluster itself (or in a GitOps tool like Argo CD or Flux).

Helm Hooks

Helm supports lifecycle hooks that Kustomize cannot replicate:

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    "helm.sh/hook": pre-upgrade
    "helm.sh/hook-weight": "0"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: my-app:1.0.0
          command: ["./migrate.sh"]
      restartPolicy: Never

This Job runs before every helm upgrade, ensuring database migrations happen before the new version is deployed.

Using Both Together

Many teams combine Helm and Kustomize:

  1. Use Helm to install third-party charts (databases, monitoring, ingress controllers)
  2. Use Kustomize to manage their own application manifests with environment overlays
  3. Use helm template to render Helm charts into plain YAML, then use Kustomize to apply additional patches
# Render Helm chart and pipe to Kustomize
helm template my-release bitnami/postgresql -f values.yaml > base/postgresql.yaml

This hybrid approach gives you the best of both worlds — the rich ecosystem of Helm charts and the simplicity of Kustomize patches for your own code.

GitOps Integration

Both tools integrate with GitOps platforms, but Kustomize is often preferred because its output is deterministic and easy to diff in pull requests. Argo CD and Flux support both Helm charts and Kustomize overlays natively.

Use Helm when...

  • You're distributing an application for others to install (like a public chart)
  • You need release management with upgrade and rollback tracking
  • Your application has complex parameterization with many configurable values
  • You want to leverage the ecosystem of existing charts (PostgreSQL, Redis, NGINX)
  • You need lifecycle hooks (pre-install, post-upgrade jobs)

Use Kustomize when...

  • You want to customize existing manifests without adding template syntax
  • You're managing environment-specific variations (dev, staging, prod)
  • You want to keep your YAML valid and readable without template markers
  • You're using GitOps and want plain YAML that is easy to diff and review
  • You need simple overlays on top of upstream manifests

Model Interview Answer

Helm and Kustomize take fundamentally different approaches to managing Kubernetes manifests. Helm is a package manager — it uses Go templates to generate YAML from parameterized charts. You define defaults in values.yaml and override them per environment. Helm also tracks releases and supports rollbacks. Kustomize uses a template-free approach — you write plain Kubernetes YAML as a base and apply overlays and patches for different environments. Kustomize is built into kubectl and keeps manifests readable. Many teams use both: Helm charts for third-party applications (databases, monitoring) and Kustomize for their own application manifests where full templating is unnecessary.

Related Comparisons