Skip to content
8 min read·Lesson 8 of 8

Helm vs Kustomize, Helmfile, and GitOps

Where Helm stops being the right tool: comparing with Kustomize, layering with Helmfile, and integrating with Argo CD or Flux for GitOps.

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 apply directly.
  • 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

ScenarioRecommended
You are publishing a reusable component for others to consumeHelm
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 environmentsKustomize
You need a small patch to a vendor chart you can't forkHelm + post-render Kustomize
You want a typed, programmable modelcdk8s / 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

  1. Are you publishing for external consumers? → Helm chart on OCI.
  2. Are you deploying first-party services to your own clusters? → Kustomize, optionally rendered by Argo CD or Flux.
  3. Are you deploying a vendor product? → Their Helm chart, with a Kustomize post-render if needed.
  4. 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

Key Takeaways

  • Helm shines when you publish or consume third-party packages; Kustomize shines for your own apps with environment overlays.
  • helm template + kustomize is a powerful hybrid — render then patch.
  • Helmfile orchestrates multiple releases declaratively in one file.
  • Argo CD and Flux can both render charts; let the controller reconcile rather than running helm in CI.
  • Pick the lightest tool that meets your needs — every layer is a future debugging tax.
🎉

Course Complete!

You've finished Helm and Kubernetes Packaging. Now put your knowledge to the test with real exam-style practice questions.