Helm is excellent, but it is not always the right answer. This lesson surveys the surrounding tools and helps you decide where each fits.
Kustomize: The Other Option
Kustomize is built into kubectl (kubectl apply -k ./) and takes a fundamentally different approach. Instead of templating, it patches plain YAML using overlays:
my-app/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml # patches base
│ └── replica-patch.yaml
├── staging/
└── prod/
Render an environment:
kubectl kustomize ./overlays/prod
kubectl apply -k ./overlays/prod
Strengths
- No templating — the base files are real Kubernetes YAML you can
kubectl applydirectly. - No magic Go-template whitespace to debug.
- Composable patches: JSON 6902 or strategic merge.
- Native to kubectl; nothing else to install.
Weaknesses
- No packaging or versioning — overlays are files in your repo, not artifacts.
- No release lifecycle, hooks, or rollback.
- Sharing complex configuration across teams is awkward.
- Patches at scale become as opaque as templates.
The Right Default
| Scenario | Recommended |
|---|---|
| You are publishing a reusable component for others to consume | Helm |
| You are deploying a vendor product (Prometheus, Cert-Manager) | Helm (you don't have a choice; they ship a chart) |
| You are deploying first-party services across environments | Kustomize |
| You need a small patch to a vendor chart you can't fork | Helm + post-render Kustomize |
| You want a typed, programmable model | cdk8s / Pulumi / jsonnet |
Helm + Kustomize: The Hybrid
Helm 3 supports post-rendering: render the chart, then pipe the manifests through any binary that reads YAML on stdin and writes YAML on stdout. Pair this with Kustomize for surgical patches to vendor charts:
# kustomization.yaml
resources: [all.yaml]
patches:
- target: { kind: Deployment, name: vendor-app }
patch: |
- op: add
path: /spec/template/spec/tolerations
value: [{key: "dedicated", operator: "Equal", value: "platform", effect: "NoSchedule"}]
helm install vendor ./vendor-chart \
--post-renderer ./kustomize-postrender.sh
The script:
#!/bin/bash
cat > all.yaml
kustomize build . --enable-helm
This pattern is the standard way to not fork upstream charts.
Helmfile: Orchestrating Multiple Releases
Helmfile is a declarative wrapper around Helm. One file describes every release across an environment:
releases:
- name: cert-manager
namespace: cert-manager
chart: oci://quay.io/jetstack/charts/cert-manager
version: v1.16.1
values: [values/cert-manager.yaml.gotmpl]
- name: ingress-nginx
namespace: ingress-nginx
chart: oci://ghcr.io/nginxinc/charts/nginx-ingress
version: 4.11.3
values: [values/ingress-nginx.yaml.gotmpl]
needs: [cert-manager/cert-manager] # ordering
- name: my-app
namespace: web
chart: oci://ghcr.io/acme/charts/my-app
version: 1.4.2
values: [values/my-app.{{ .Environment.Name }}.yaml]
needs: [ingress-nginx/ingress-nginx]
helmfile -e prod diff
helmfile -e prod apply
helmfile -e prod destroy
Combine with helm-secrets / SOPS for encrypted values files in Git. Helmfile is most useful for ad-hoc environments and bootstrapping; for ongoing reconciliation, prefer GitOps.
GitOps: Argo CD and Flux
The modern production pattern is to let a controller in the cluster reconcile Git to live state. Both Argo CD and Flux understand Helm charts natively:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: oci://ghcr.io/acme/charts
chart: my-app
targetRevision: 1.4.2
helm:
valueFiles: [values-prod.yaml]
destination:
server: https://kubernetes.default.svc
namespace: web
syncPolicy:
automated: { prune: true, selfHeal: true }
syncOptions: [CreateNamespace=true]
Argo CD renders the chart with helm template, then applies and reconciles. You never run helm install directly. Benefits:
- Drift detection — the controller surfaces any divergence from Git.
- Self-heal — manual changes are reverted (or alerted).
- RBAC and audit live in the controller, not in operator kubectl access.
- Multi-cluster fan-out from one Git repo.
Flux's HelmRelease CRD provides the same model. Both support OCI sources and chart pulling.
When Helm Hooks Don't Translate
Argo CD has its own argocd.argoproj.io/hook annotations and ignores Helm's by default (it does not helm install — it renders and applies). Flux runs Helm itself so hooks work. Know which tool you are in before relying on hook behaviour.
Decision Tree
- Are you publishing for external consumers? → Helm chart on OCI.
- Are you deploying first-party services to your own clusters? → Kustomize, optionally rendered by Argo CD or Flux.
- Are you deploying a vendor product? → Their Helm chart, with a Kustomize post-render if needed.
- Are you orchestrating a stack of releases together? → Argo CD ApplicationSet / App-of-Apps, or Helmfile for bootstrap.
The principle: pick the lightest tool that meets the need. Every additional layer is something a future engineer must debug at 3am.
Where to Go Next
- Practise on the CNCF CKAD exam — Helm is tested.
- Study production charts: Bitnami, Prometheus Community.
- Read the official Helm best practices.
- Take the CertQnA Kubernetes Basics and Platform Engineering & IDPs courses to round out the picture.