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.
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.
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.
Argo Rollouts vs Flagger — Which Canary Deployment Tool Should You Use? (2026)
Both Argo Rollouts and Flagger do progressive delivery on Kubernetes. Here's a detailed comparison of features, architecture, and when to pick each.