Intro
Kubernetes Kustomization is a powerful built-in feature for managing Kubernetes manifests without templates. It allows operators to declare variations of a base configuration for different environments. However, as the number of overlays, patches, and resources grows, Kustomization performance can degrade, leading to slower builds, delayed rollouts, and increased API server load. This guide provides a practical, hands-on approach to diagnosing and tuning Kustomization performance issues.
This article is intended for developers, DevOps engineers, and technical startup teams who use Kustomize and need to resolve latency bottlenecks or optimize their configuration pipeline. By following the steps here, you will learn how to observe current behavior, make small controlled changes, and verify improvements using concrete commands and expected outputs.
The guiding principle is operational safety: do not make blind changes. Instead, establish a baseline, understand the blast radius of any modification, avoid exposing secrets, and have a clear recovery path if a change does not produce the expected result.
Version and Environment Inventory
Before attempting any performance tuning, you must know your exact environment. This section covers the essential information you need to collect.
What to Record
- Kubectl version:
kubectl version --client --output=yamlandkubectl version --server --output=yaml(if you have direct access to the control plane). - Kustomize version:
kubectl kustomize version(if using the built-in kubectl kustomize) orkustomize versionif you have the standalone binary. - Cluster version and topology: The Kubernetes server version and the number of nodes, control plane endpoints, and whether you are using a managed service (EKS, GKE, AKS) or a self-managed cluster.
- Deployment tooling: Which tools are used to apply Kustomizations, such as Argo CD, Flux, or plain
kubectl apply -k. - Resource counts: How many resources are generated per Kustomization, and how many total Kustomizations exist.
Example Inventory Commands
Run these read-only commands first to capture the current state:
# Save current state for later comparison
kubectl get nodes -o wide > nodes-before.txt
kubectl get pods -A -o wide > pods-before.txt
kubectl get kustomizations -A -o yaml > kustomizations-before.yaml # if using Flux
kubectl get applications -A -o yaml > applications-before.yaml # if using Argo CD
Check the Kustomize version:
kubectl kustomize version
# Example output:
# v4.5.7
If you are using a standalone kustomize binary:
kustomize version
# Example output:
# {Version:kustomize/v4.5.7 GitCommit:56d82a8378dfc8dc3b3b1085e5a6e67b82966bd7 BuildDate:2022-08-02T16:35:54Z GoOs:linux GoArch:amd64}
Identify the Baseline
Choose one specific Kustomization that exhibits slow performance. For example, suppose you have a Kustomization for a web application with 200 resources (Deployments, Services, ConfigMaps, etc.). Record the current build and apply times:
# Time the kustomize build (standalone)
time kustomize build ./path/to/overlay > /tmp/output.yaml
# Example output:
# real 0m12.344s
# user 0m10.121s
# sys 0m1.223s
# Time the kubectl apply with kustomize
kubectl create namespace test-ns --dry-run=client -o yaml | kubectl apply -f -
time kubectl apply -k ./path/to/overlay -n test-ns
If using Argo CD or Flux, look at the reconciliation duration in the UI or logs.
Prerequisites and Constraints
- You need
kubectlconfigured with cluster-admin or at least permissions to read all resources in the namespace you are testing. - Ensure you are not testing on a production cluster without proper safeguards. Use a dedicated test namespace or a staging cluster.
- Kustomize performance can be affected by the number of files, the complexity of transformers, and the network latency to the API server. Before changing anything, understand your environment's limitations (e.g., CPU and memory of the CI runner or controller).
Safe Configuration Path
Once you have a baseline, the next step is to make small, incremental changes and measure their impact. This section outlines a safe approach to tuning Kustomization performance.
Observation Before Intervention
Never change multiple variables at once. If you suspect that a specific transformer or feature is causing slowness, test it in isolation. For example, if you are using a large number of patches (patchesStrategicMerge or patches), measure the build time with and without those patches.
Example: Suppose your overlay contains a kustomization.yaml with:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- patch.yaml
- patch2.yaml
# ... many more
Measure the build time:
time kustomize build . > /dev/null
Then, temporarily comment out the patches (or move them to a separate directory) and measure again. If the build time drops significantly, you have found a contributor.
Smallest Justified Change
If the patches are necessary, consider alternative ways to apply them. For example, use the newer patches field with explicit target selectors instead of patchesStrategicMerge, or combine multiple patches into one to reduce file I/O. Also, check if any patch is redundant because it is already included in the base.
Example: Convert from patchesStrategicMerge to inline patches:
Before:
patchesStrategicMerge:
- deployment-patch.yaml
After:
patches:
- target:
kind: Deployment
name: web
patch: |-
- op: replace
path: /spec/replicas
value: 3
This reduces file reads and can speed up builds.
Verify the Outcome
After making a change, rebuild and measure the time. Also verify that the generated output is correct by comparing it with the original (using diff or kubectl diff).
time kustomize build . > /tmp/new-output.yaml
kubectl diff -k . -n test-ns
If the diff is clean (no unexpected changes), and the build time improved, the change is safe.
Recovery Path
If the change causes issues (e.g., wrong output or longer build time), revert to the previous configuration immediately. Since you are using version control, this should be a simple git revert. Always commit your working state before experimenting.
Verification and Diagnostics
This section dives deeper into diagnostic techniques to identify Kustomization performance bottlenecks.
Profiling Kustomize Build
Kustomize build performance can be profiled using Go's built-in profiling if you have the standalone binary. Run:
kustomize build --profile ./cpu.pprof . > /dev/null
go tool pprof -http=:8080 ./cpu.pprof
This will open a web interface showing CPU usage by function, helping you pinpoint hotspots (e.g., deep copy operations, YAML parsing, transformer loops).
If you cannot use profiling, use a simpler approach: gradually remove parts of the configuration to isolate the slow component. For example, start with only the resources field, then add generators, then transformers, measuring time at each step.
Measuring API Server Load
Kustomization performance also affects the apply side. When you run kubectl apply -k, kustomize builds the manifest and then sends it to the API server. If the manifest is large, the apply can be slow. Check the API server metrics for request latency and request size.
If you have access to the Kubernetes control plane metrics (e.g., via Prometheus), look at:
apiserver_request_duration_secondsapiserver_request_totalapiserver_response_sizes_bytes
A spike in these metrics during an apply indicates a large or complex manifest.
To reduce the manifest size, consider using server-side apply or splitting the Kustomization into smaller parts. Server-side apply sends a patch rather than the full object, which can reduce payload size.
Logs and Events
When using GitOps tools like Flux or Argo CD, check their logs for reconciliation duration or errors:
kubectl logs -n flux-system deploy/source-controller | grep -i kustomize
# or for Argo CD:
kubectl logs -n argocd deploy/argocd-repo-server | grep -i kustomize
These logs often show the time taken for the build step. For example, Flux's kustomize-controller logs include a duration field for each reconciliation.
Failure Modes and Recovery
Understanding common failure modes helps you quickly recover when something goes wrong.
Build Timeout or OOM
If kustomize build takes too long or runs out of memory, it may be due to an extremely large number of resources or an infinite loop caused by a misconfigured configMapGenerator or secretGenerator. For example, a configMapGenerator with a glob that matches thousands of files can cause high memory usage.
Recovery: Reduce the scope of the generator. Use specific file names instead of wildcards, or split the configuration into multiple Kustomizations.
Inconsistent Output Between Builds
Sometimes, kustomize produces different output for the same input due to map ordering or legacy features. This can cause unnecessary diffs and repeated applies. Ensure you are using a consistent kustomize version and enable --load-restrictor LoadRestrictionsRootOnly only when needed, as it can affect file loading.
Apply Fails with Resource Limits
If the generated manifest exceeds the API server's maximum request size (default ~3 MB), the apply will fail with 413 Request Entity Too Large. This often happens with large ConfigMaps or Secrets.
Recovery: Split the Kustomization or use kubectl create with server-side apply to break the request into smaller chunks. Alternatively, store large data in a CSI volume or use a different mechanism.
Incorrect Resource Ordering
Kustomize may not apply resources in the desired order, which can cause temporary failures. Use kubectl apply --server-side with field managers or add configuration annotations to define dependencies.
Operations Checklist
Use this checklist to systematically tune and maintain Kustomization performance.
| Item | Owner | Frequency | Verification Command | Expected Result |
|---|---|---|---|---|
| Record kustomize and kubectl versions | DevOps Lead (e.g., Priya Shah) | Monthly or after any upgrade | kustomize version && kubectl version --short | Output matches documented baseline |
| Measure build time for representative Kustomizations | CI/CD Engineer | Weekly | time kustomize build ./overlay | Build time within SLO (e.g., < 10s for 200 resources) |
| Check API server request size during applies | Platform Engineer | Before each release | Prometheus query: apiserver_response_sizes_bytes{verb="PATCH"} | < 1 MB per request |
| Audit generator usage (configMapGenerator, secretGenerator) | Security Lead | Quarterly | Review kustomization files for wildcards; kustomize build --load-restrictor LoadRestrictionsRootOnly test | No unexpected files included |
| Test failover and recovery procedure | SRE | Semi-annually | Simulate a failed apply and rollback; document steps | Rollback completes within 10 minutes |
| Clean up unused resources and patches | Developers | Monthly | kustomize build then kubectl diff to spot removed resources | No orphaned resources in cluster |
Common Pitfalls and How to Avoid Them
Ignoring Version Compatibility
Pitfall: Using a kustomization.yaml with features that are not supported by your kustomize version, leading to build errors or silent fallback.
Why it happens: Different kustomize versions have different transformer implementations and deprecations. For example, patchesStrategicMerge was deprecated in favor of patches in kustomize v4+.
How to avoid: Pin your kustomize version in CI/CD and local tooling. Always check the release notes when upgrading. Run a build test in a CI pipeline with the exact version.
Overusing Remote Bases
Pitfall: Using remote bases (e.g., GitHub URLs) in resources can slow down builds due to network fetching and lack of caching.
Why it happens: Remote bases are convenient but introduce latency and potential failure points.
How to avoid: Vendor remote bases into your repository and reference them locally. If you must use remote bases, set up a caching proxy or use a tool like kustomize build with --load-restrictor LoadRestrictionsNone only in trusted environments.
Excessive Use of Transformers
Pitfall: Using many namePrefix, nameSuffix, commonLabels, and images transformers can increase CPU usage, especially on large resource sets.
Why it happens: Each transformer iterates over all resources, and complex image name transformations involve string parsing and map lookups.
How to avoid: Combine transformers where possible and apply them at the base level instead of repeating in each overlay. For image updates, consider using kustomize edit set image sparingly or use a tool like kustomize-sops to manage image tags.
Not Measuring Before and After
Pitfall: Making changes based on guesswork and not verifying improvement.
Why it happens: It is tempting to apply known best practices without testing their impact in your specific context.
How to avoid: Always run controlled experiments: change one variable, measure build time and apply time, compare with baseline, and keep or revert based on data. Use a script or CI job to automate this.
Building on Every Commit Without Caching
Pitfall: In a GitOps pipeline, rebuilding the entire Kustomization for every commit wastes CPU and slows down reconciliation.
Why it happens: Many controllers (e.g., Flux) rebuild when source changes, but if the source includes unused files or the build is not optimized, it becomes slow.
How to avoid: Use .sourceignore or .kustomizeignore to exclude irrelevant files. Enable controller caching if available. Split large Kustomizations into smaller ones that can be reconciled independently.
Conclusion
Kubernetes Kustomization performance tuning requires a methodical approach: gather version and environment data, establish a baseline, make minimal changes, verify outcomes, and have recovery plans. The concrete commands and examples in this guide should help you move from observed problem to verified result.
To start improving your Kustomization performance today, pick one low-risk verification from the Operations Checklist and run it in a test namespace. Record the before and after metrics for build time and API server load. Share your findings with your team and document any adjustments to your tuning process.
A reliable operational workflow makes performance visible, limits risk, and ensures that every change is evidence-based. With these practices, you can keep your Kubernetes deployments fast and stable.