When I first started running production workloads on GKE, I thought managing Kubernetes manifests would be the easy part. Just write some YAML, deploy it, and move on.
That illusion lasted about two environments.
Very quickly, I found myself dealing with the same application deployed to dev, staging, and prod, each with slightly different settings. Different image tags, different replica counts, different ingress hosts, different resource limits. The core workload was the same, but the configuration kept drifting.
At first, I did what most people do: copy folders and tweak values manually. Then I tried Helm. Then I realized both approaches were solving different problems than the one I actually had.
That’s when I landed on Kustomize as the right level of abstraction for most GKE workloads.
GKE makes it very easy to spin up clusters, but it doesn’t solve the configuration problem for you.
In real systems, you usually have:
Multiple environments (dev, staging, prod)
Multiple clusters or projects
The same service deployed everywhere
Slight but important differences between environments
The problem is not writing YAML. The problem is maintaining consistency over time without duplication.
This is where most teams either:
Copy-paste manifests
Or introduce complex templating systems too early
Both approaches create long-term operational debt.
Kustomize is often described as a “configuration management tool for Kubernetes”, but that’s too vague to be useful.
In simple terms, Kustomize lets you:
Define a base configuration that represents the core workload
Define overlays that modify only what needs to change per environment
Compose everything together without templates or variables
The most important mental shift is this:
Kustomize is about composition, not generation.
It doesn’t generate YAML from logic.
It layers and modifies existing YAML in a predictable way.
This turns out to be exactly what most GKE teams need.
GKE environments usually follow a fairly consistent pattern:
Same cluster architecture
Same namespaces
Same service structure
Different scale and exposure
What changes is typically:
Replica counts
Resource limits
Ingress settings
Image versions
Node pool placement
Kustomize works well here because it lets you keep the “what” stable and only adjust the “how much” and “where”.
It also integrates nicely with the GKE ecosystem:
Native support in kubectl
Works with GitHub Actions, Cloud Build, ArgoCD, Flux
No extra runtime or controllers required
It’s just configuration logic, which is a big operational advantage.
This is the single most important concept in Kustomize.
The base represents:
What the service is
What never changes
The core deployment, service, config maps
The overlays represent:
Where it runs
How it behaves in that environment
The differences that must exist
In practice, this usually means:
The base contains:
Deployment
Service
Health probes
Default resource requests
The overlays contain:
Image tags
Replicas
Ingress hostnames
Environment-specific annotations
Higher resource limits in prod
This separation forces you to think clearly about what is truly environment-specific and what is not. That alone improves design quality.
In most cases, I keep Kustomize inside the application repo.
A typical structure looks like:
/base
/overlays/dev
/overlays/staging
/overlays/prod
Each overlay has its own kustomization.yaml that references the base and applies patches.
This keeps everything:
Versioned with the code
Easy to review
Easy to promote between environments
For platform teams, this also fits nicely with golden paths and service templates, because the structure is predictable and repeatable.
This is where many discussions become ideological. I prefer to be pragmatic.
Helm is great when:
You need to package complex software
You consume third-party charts
You need parameterized releases
Kustomize is better when:
You control the application
You want full transparency
You care about diff readability
You want minimal abstraction
In most GKE platforms I’ve worked on, the pattern ends up being:
Helm for vendor software (Prometheus, Istio, cert-manager)
Kustomize for internal services
That combination works very well.
The biggest mistake is overusing overlays.
I’ve seen setups with:
10+ overlays
Deeply nested patches
No clear base
At that point, you’ve recreated the complexity you were trying to avoid.
Another common mistake is using Kustomize to manage secrets directly. That usually leads to either committing secrets to Git or building fragile workflows. Kustomize should reference secrets, not store them.
Finally, many teams treat Kustomize as a replacement for validation and policy. It’s not. You still need:
OPA or admission policies
Resource quotas
Naming conventions
Platform guardrails
Kustomize helps with structure, not governance.
In most real pipelines, Kustomize is just one step.
A typical flow looks like:
Build container image
Push to Artifact Registry
Update image tag in overlay
Run kustomize build
Apply to cluster
The key advantage is that you always deploy fully resolved manifests. There is no hidden templating logic at runtime. What you see in Git is what goes to the cluster.
This makes:
Rollbacks easier
Reviews safer
Debugging simpler
This is where Kustomize really shines for platform engineering.
Service golden paths often use Kustomize because:
The base encodes platform standards
The overlays encode team-specific choices
Policy engines can validate the final output
Platform teams can enforce structure without blocking autonomy
It fits perfectly with an IDP model where:
The platform defines the paved road
Teams stay within guardrails
Everything remains Git-native
There is a point where Kustomize starts to feel limited.
This usually happens when:
You need cross-service orchestration
You manage hundreds of environments
You need dynamic infrastructure composition
At that point, you start looking at:
GitOps controllers
Crossplane
Terraform + Helm
Higher-level abstractions
But that’s a maturity problem. Most teams are not there, and they shouldn’t pretend they are.
I still use Kustomize because it keeps Kubernetes boring.
It doesn’t introduce:
New runtimes
New control planes
New mental models
It simply enforces good structure and makes environment differences explicit.
For most GKE teams, that’s exactly what you want:
predictable deployments, transparent configuration, and just enough abstraction to stay sane.
Not magic. Just good engineering.