Helmfile vs Kustomize for Multi-Environment Kubernetes Deployments
Both Helmfile and Kustomize manage environment-specific Kubernetes configurations, but they take completely different approaches. Here's the practical comparison with real config examples.
You need to deploy the same app to dev, staging, and prod — but with different replicas, resource limits, and config values in each environment. You have two popular tools: Helmfile and Kustomize. Here's how they differ.
The Core Difference
Kustomize — Patch-based. Start with a base configuration, apply overlays (patches) for each environment. Pure YAML, built into kubectl.
Helmfile — Declarative Helm. A single file defines which Helm charts deploy to which environments with which values. Orchestrates multiple Helm releases.
Kustomize — Base + Overlays
Kustomize directory structure:
k8s/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── patch-replicas.yaml
├── staging/
│ ├── kustomization.yaml
│ └── patch-resources.yaml
└── production/
├── kustomization.yaml
└── patch-production.yaml
Base deployment:
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api
spec:
replicas: 1
template:
spec:
containers:
- name: my-api
image: my-api:latest
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yamlProduction overlay:
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: production
namePrefix: prod-
commonLabels:
environment: production
patches:
- patch-production.yaml
images:
- name: my-api
newTag: v1.5.2 # Pin specific version# overlays/production/patch-production.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api
spec:
replicas: 5 # Override replicas
template:
spec:
containers:
- name: my-api
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"# Apply production
kubectl apply -k overlays/production/
# Preview what will be applied
kubectl kustomize overlays/production/
# Dry run
kubectl apply -k overlays/production/ --dry-run=clientKustomize strengths:
- Built into kubectl (no extra tool)
- Native ArgoCD support
- Pure YAML — no templating language to learn
- Easy to see exactly what changes per environment
Kustomize weaknesses:
- Complex transformations require strategic merge patches
- Managing many environments gets repetitive
- Not great for deploying external Helm charts with your overlays
- ConfigMap/Secret generation is limited
Helmfile — Declarative Helm Orchestration
Helmfile manages multiple Helm releases in a single declarative file.
# helmfile.yaml
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
- name: my-charts
url: https://charts.mycompany.com
environments:
dev:
values:
- environments/dev/values.yaml
staging:
values:
- environments/staging/values.yaml
production:
values:
- environments/production/values.yaml
releases:
- name: my-api
namespace: "{{ .Environment.Name }}"
chart: my-charts/my-api
version: 1.5.2
values:
- values/my-api-common.yaml
- values/my-api-{{ .Environment.Name }}.yaml
- name: redis
namespace: "{{ .Environment.Name }}"
chart: bitnami/redis
version: 19.0.0
values:
- values/redis-{{ .Environment.Name }}.yaml
condition: redis.enabled # Only deploy if redis.enabled=true
- name: postgresql
namespace: "{{ .Environment.Name }}"
chart: bitnami/postgresql
version: 14.0.0
installed: "{{ ne .Environment.Name "dev" }}" # Skip in devEnvironment-specific values:
# environments/production/values.yaml
redis:
enabled: true
# values/my-api-production.yaml
replicaCount: 5
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2"
memory: 2Gi
ingress:
enabled: true
host: api.mycompany.com
autoscaling:
enabled: true
minReplicas: 5
maxReplicas: 20# Install helmfile
brew install helmfile
# Sync all releases to production
helmfile -e production sync
# Preview changes
helmfile -e production diff
# Deploy specific release only
helmfile -e production --selector name=my-api sync
# Destroy everything (careful!)
helmfile -e production destroyHelmfile strengths:
- Manages multiple Helm releases as one unit
- Conditional deployments (skip redis in dev, include in prod)
- Works with any Helm chart (your own + external)
- Templating with Go templates for complex logic
- Supports hooks (pre/post sync scripts)
Helmfile weaknesses:
- Extra tool to install and maintain
- Not natively supported by ArgoCD (needs the helmfile plugin)
- Go template syntax can be confusing
- Harder to read than pure YAML
Side-by-Side Comparison
| Kustomize | Helmfile | |
|---|---|---|
| Templating | ❌ Patches only | ✅ Go templates |
| kubectl built-in | ✅ | ❌ Separate tool |
| ArgoCD native | ✅ | ⚠️ Plugin needed |
| Multi-chart orchestration | ❌ | ✅ Core feature |
| External Helm charts | ⚠️ Difficult | ✅ Native |
| Learning curve | Low | Medium |
| YAML verbosity | Medium | Low |
| Conditional deployments | ⚠️ Workarounds | ✅ Native |
When to Use Each
Use Kustomize when:
- You're managing your own Kubernetes manifests (not Helm charts)
- You want ArgoCD GitOps without extra plugins
- Environments differ only in replicas, images, resource sizes
- You want the simplest possible solution
- Team is new to Kubernetes — lower learning curve
Use Helmfile when:
- You're deploying multiple external Helm charts (postgres, redis, ingress) plus your own
- Environments have structural differences (dev has no redis, prod does)
- You need conditional deployments or complex environment logic
- You want to orchestrate an entire application stack in one file
The most common pattern in 2026:
- Kustomize for your own app manifests
- Helmfile or Helm + ArgoCD for the full application stack including third-party charts
Kustomize + Helm Together
You can use Kustomize to patch Helm chart output:
# kustomization.yaml
helmCharts:
- name: my-api
repo: https://charts.mycompany.com
version: 1.5.2
releaseName: my-api
valuesFile: values.yaml
patches:
- target:
kind: Deployment
name: my-api
patch: |-
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: EXTRA_ENV_VAR
value: "production-specific"For Helm, Kustomize, and GitOps hands-on labs, KodeKloud has courses covering multi-environment deployment patterns with real Kubernetes clusters.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Build a GitOps Pipeline with ArgoCD and Helm (2026 Project Walkthrough)
A complete GitOps setup with ArgoCD and Helm — from installing ArgoCD to auto-deploying your app when you push to Git.
Helm vs Kustomize vs Jsonnet: Which Config Tool Should You Use in 2026?
Helm, Kustomize, and Jsonnet all solve Kubernetes config management differently. Here's an honest comparison to help you pick the right one for your team.
How to Set Up FluxCD for GitOps on Kubernetes from Scratch (2026)
Step-by-step guide to installing FluxCD, bootstrapping it with GitHub, setting up Kustomizations and HelmReleases, and managing multi-environment deployments with GitOps.