Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Kustomize: Template-Free Kubernetes Config

By Kokil Thapa | Last reviewed: August 2026

Managing YAML across dev, staging, and production often leads to copy-paste drift and fragile sed scripts. Kustomize: Template-Free Kubernetes Config solves this by treating configuration as data rather than code, allowing you to layer environment-specific changes over a clean base manifest. If you are tired of maintaining three slightly different copies of the same deployment file or debugging nested Helm conditionals, this declarative overlay approach offers a native, GitOps-friendly alternative that integrates directly with kubectl. For teams building scalable and efficient systems, eliminating template logic reduces cognitive load and makes infrastructure reviews significantly faster.

How does Kustomize: Template-Free Kubernetes Config actually work?

Unlike Helm or Jsonnet, Kustomize does not use a templating engine. There are no variables, loops, or if-statements embedded in your manifests. Instead, it uses a patching mechanism where you define a "base" set of canonical Kubernetes resources and then apply "overlays" that modify specific fields for each target environment. This distinction matters because your base YAML remains valid, standard Kubernetes API objects at all times. You can validate, lint, and test the base independently of any customization.

The core unit of work is the kustomization.yaml file. This metadata file declares which resources belong to the current layer and lists any transformations to apply. When you run kubectl apply -k, the Kustomize engine reads this file, loads the referenced resources, applies patches and generators in memory, and emits the final rendered YAML to the cluster. Nothing is written back to disk unless you explicitly redirect output, keeping your source repository clean.

Base Layerdeployment.yamlservice.yamlconfigmap.yamlDev Overlayreplicas: 1debug: trueProd Overlayreplicas: 5resources: highFinal OutputValid K8s YAMLEnvironment Ready
Kustomize merges a shared base with environment-specific overlays to produce valid Kubernetes manifests without templating

This architecture aligns well with GitOps workflows. Because every environment is just a directory with a kustomization.yaml pointing to the same base, pull requests show exactly what differs between environments. There is no hidden state or external variable store to synchronize. When I audit infrastructure for clients, this transparency consistently speeds up security reviews compared to parameterized templates where critical values can be buried in deeply nested logic.

How do you structure a Kustomize project for multiple environments?

A common mistake is creating separate bases for each environment or flattening everything into one directory. The most maintainable structure separates concerns strictly: one canonical base, and one overlay directory per environment. This ensures that changes to application structure happen in one place, while environment tuning stays isolated.

<project-root>/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── namespace.yaml
├── overlays/
│   ├── dev/
│   │   ├── kustomization.yaml
│   │   └── replica-patch.yaml
│   ├── staging/
│   │   ├── kustomization.yaml
│   │   └── resource-patch.yaml
│   └── prod/
│       ├── kustomization.yaml
│       ├── hpa.yaml
│       └── ingress-patch.yaml
└── README.md

Your base kustomization.yaml should list only the raw resources needed to run the application in its most generic form. Avoid putting environment-specific labels, annotations, or resource limits here. Those belong in overlays.

# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - namespace.yaml
  - deployment.yaml
  - service.yaml

commonLabels:
  app.kubernetes.io/name: myapp
  app.kubernetes.io/managed-by: kustomize

Each overlay references the base and adds its own modifications. The dev overlay might reduce replicas and enable debug logging, while prod increases resources and adds an HPA. Crucially, overlays can also reference other overlays. A "prod-us-east" overlay could inherit from "prod" and only change region-specific annotations, avoiding duplication even within the same tier.

Using strategic merge patches vs JSON patches

Kustomize supports two patching strategies. Strategic merge patches are YAML files that look like partial Kubernetes objects. They are intuitive and cover 90% of use cases. JSON patches (RFC 6902) are more powerful but verbose; use them only when you need to manipulate arrays by index or perform operations strategic merge cannot handle, like removing a specific container from a pod spec.

# overlays/prod/resource-patch.yaml (Strategic Merge)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 5
  template:
    spec:
      containers:
        - name: app
          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"
            limits:
              cpu: "1000m"
              memory: "1Gi"

In practice, stick to strategic merge patches unless you hit a specific limitation. They are easier to read in code review and less prone to breaking when upstream manifests add new fields. JSON patches are brittle; a reordered array in the base can silently apply your patch to the wrong element.

Kustomize vs Helm: Which configuration tool should you choose in 2026?

This question comes up on nearly every CI/CD pipeline setup engagement. Both tools solve configuration management, but they optimize for different trade-offs. Understanding these differences prevents costly rewrites later. While Helm dominates the ecosystem for distributing third-party software, Kustomize excels for managing first-party application configurations where simplicity and Git-native workflows matter more than packaging flexibility.

CriteriaKustomizeHelm
Learning CurveLow. Pure YAML, no new language.Moderate. Go templates, chart structure, values schema.
Templating LogicNone. Declarative patches only.Full Go templates with conditionals and loops.
Base Manifest ValidityAlways valid K8s YAML.Templates are invalid until rendered.
Third-Party DistributionPoor. No registry or versioned packages.Excellent. OCI registries, versioning, dependencies.
GitOps CompatibilityNative. Files are the source of truth.Requires rendering step or controller support.
Complex ConditionalsNot supported. Use multiple overlays.Native support via if/else in templates.
Built into kubectlYes (-k flag).No. Separate CLI required.

Choose Kustomize when your team owns the application code and configuration together, especially if you want PR diffs to show exact YAML changes. Choose Helm when packaging software for others to consume, or when you genuinely need programmatic generation of manifests based on complex input matrices. Many mature organizations use both: Helm for infrastructure primitives (ingress controllers, cert-manager) and Kustomize for application deployments. This hybrid approach leverages each tool's strengths without forcing compromises.

Start: Config NeedDistributing to others?YESNOUse HelmNeed complex logic?YESNOUse HelmKustomize
Decision tree for selecting Kustomize vs Helm based on distribution needs and configuration complexity

How do you manage secrets and dynamic values without templates?

The absence of variable interpolation is Kustomize's greatest strength and most frequent pain point. You cannot write {{ .Values.dbPassword }} in a manifest. Instead, Kustomize provides secretGenerator and configMapGenerator resources that create these objects at build time from local files or environment variables. This keeps sensitive data out of version control while maintaining declarative configuration.

# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

secretGenerator:
  - name: app-secrets
    files:
      - db-password.txt
    options:
      disableNameSuffixHash: true

configMapGenerator:
  - name: app-config
    literals:
      - LOG_LEVEL=info
      - CACHE_TTL=3600
    options:
      disableNameSuffixHash: true

Note the disableNameSuffixHash: true option. By default, Kustomize appends a hash to generated resource names to trigger rollouts when content changes. This is useful for immutable configs but problematic for secrets referenced by name in deployments. Disabling the suffix gives you predictable names at the cost of needing manual rollout triggers or external tooling for rotation.

For production systems, avoid committing secret files even with hashing. Use external secret operators (External Secrets Operator, Sealed Secrets, or Vault Agent Injector) alongside Kustomize. Let Kustomize handle the structural configuration while delegating secret injection to specialized tools. This separation of concerns keeps your Kustomize layers focused on application topology rather than credential management. Teams adopting modern security practices find this pattern essential for compliance audits.

Handling dynamic values with replacements

Kustomize v5 introduced the replacements transformer, which allows copying values from one resource field to another without templates. This solves cases where you need the same value in multiple places, like a service name referenced in both an Ingress and a ConfigMap.

replacements:
  - source:
      kind: Service
      name: myapp
      fieldPath: metadata.name
    targets:
      - select:
          kind: Ingress
          name: myapp-ingress
        fieldPaths:
          - spec.rules.0.http.paths.0.backend.service.name
      - select:
          kind: ConfigMap
          name: app-config
        fieldPaths:
          - data.SERVICE_NAME

Replacements are explicit and traceable. Unlike template variables that can be defined anywhere, replacement sources must exist as real resources in the current build context. This prevents undefined variable errors and makes dependencies visible in the kustomization file itself.

What are the common pitfalls when adopting Kustomize in production?

After seeing multiple teams adopt and sometimes struggle with Kustomize, several recurring issues stand out. Addressing these proactively prevents operational friction.

  1. Over-engineering overlays: Creating deep inheritance chains (base → common → env → region → cluster) makes debugging impossible. Keep hierarchies shallow, maximum two levels deep. Duplicate a little rather than abstract too much.
  2. Ignoring naming collisions: When multiple teams share a cluster, resource names collide. Always use namePrefix or namespace transformers in your base or top-level overlay to ensure uniqueness.
  3. Forgetting generator hashes: Enabling name suffix hashes on ConfigMaps referenced by volume mounts causes pods to fail mounting if the deployment doesn't also get updated. Either disable hashes for mounted configs or ensure the deployment references the generated name correctly.
  4. Mixing Helm and Kustomize incorrectly: Running helm template | kustomize build works but creates maintenance debt. Prefer using Helm for the chart and Kustomize only for post-render patches via the helmCharts field in kustomization.yaml (v5+), keeping the integration declarative.
  5. Neglecting validation: Kustomize output isn't validated against the cluster schema by default. Add kubeval or kubeconform to your CI pipeline to catch invalid patches before they reach deployment. This catches typos and API version mismatches early.
Anti-Patterns✗ Deep nesting (>2 levels)✗ Secrets in Git✗ Implicit variable passing✗ Skipping schema validationBest Practices✓ Flat overlay structure✓ External secrets operator✓ Explicit replacements✓ CI kubeconform checksValidation Pipeline Flowkustomize buildkubeconformSecurity ScanDeploy
Contrasting Kustomize anti-patterns with recommended practices and a safe CI validation pipeline

Another subtle issue arises with CRDs and custom resources. Kustomize doesn't understand CRD schemas natively, so strategic merge patches on custom resources may behave unexpectedly. Always test patches against actual CRD instances, and consider using JSON patches for complex CRD modifications where field semantics differ from standard Kubernetes objects.

Implementing Kustomize in Your Next Deployment

Kustomize: Template-Free Kubernetes Config represents a pragmatic middle ground between raw YAML duplication and full-blown templating complexity. Its strength lies in making configuration changes visible, reviewable, and reversible. Start by extracting your current environment variants into a shared base, then create minimal overlays for each target. Resist the urge to abstract prematurely; duplication is cheaper than wrong abstraction. Integrate kustomize build | kubeconform into your CI pipeline from day one to catch drift before it reaches production. As your platform matures, evaluate whether specific components actually need Helm's power, but default to Kustomize for application workloads where clarity trumps cleverness. For teams ready to modernize their deployment strategy, reach out via /contact-me to discuss how template-free configuration can streamline your Kubernetes operations.

Frequently Asked Questions

Kustomize is a Kubernetes configuration tool that uses YAML overlays instead of templates. It modifies base manifests through patches without requiring Go templating syntax, Helm charts, or DSLs. This keeps configurations as valid, lintable YAML files that work natively with kubectl apply commands.

Helm uses Go templates and chart packaging while Kustomize uses pure YAML overlays and strategic merge patches. Kustomize requires no template rendering step, making debugging simpler since you always work with valid YAML. Helm offers better release management and dependency handling, but Kustomize integrates directly into kubectl without additional CLI tools or server-side components.

Kustomize is embedded in kubectl since version 1.14 via the -k flag. You can also install standalone kustomize binaries for newer features not yet merged into kubectl. The standalone version typically supports generators, transformers, and plugins ahead of the bundled version, so many teams use both depending on feature requirements.

A typical layout includes a base directory with kustomization.yaml and shared resources, plus overlay directories like dev, staging, and production. Each overlay contains its own kustomization.yaml referencing the base path. Resources live in subdirectories, and patches go in dedicated folders. This structure enables environment-specific configs without duplicating base manifests across deployments.

Create separate overlay directories containing a kustomization.yaml that references your base using the resources field. Use patchesStrategicMerge or patchesJson6902 to modify specific fields per environment. Add namePrefix or commonLabels to differentiate resources. Each overlay inherits the base configuration and applies only necessary changes, keeping environment drift minimal and reviewable through standard git diffs.

Yes, using secretGenerator and configMapGenerator directives in kustomization.yaml. These generate resources from literal values, files, or env sources at build time rather than storing raw secrets in version control. For production, integrate with external secret managers like Sealed Secrets or External Secrets Operator, letting Kustomize reference generated placeholders while actual credentials remain encrypted or injected at runtime.

Strategic merge patches overlay YAML structures onto base resources using Kubernetes-aware merge semantics. Lists merge by identifying keys like container name rather than replacing entirely. Null values delete fields. This differs from JSON patches which use explicit operations. Strategic merges are more readable for most use cases but require understanding Kubernetes resource schemas to predict merge behavior correctly across complex nested structures.

Generators create ConfigMaps, Secrets, or custom resources dynamically during build time from files, literals, or env vars. Use them when content changes frequently or derives from external sources. Built-in generators handle common cases; custom generators via exec plugins support complex logic. Generators reduce manual YAML maintenance and enable hash-based naming for automatic rollout triggers when underlying data changes in GitOps workflows.

Kustomize itself performs structural validation during build but does not validate against live cluster state. Use kubeval or kubeconform to schema-validate output YAML. Run kustomize build piped through these validators in CI pipelines before deployment. For policy enforcement, integrate OPA Gatekeeper or Kyverno post-build. Always test overlay builds locally with kustomize build before committing to prevent malformed manifests reaching production clusters.

Kustomize works natively with ArgoCD and Flux since both support kustomization.yaml as a sync source. Store base and overlays in Git, let the GitOps controller run kustomize build server-side. Use image updaters to modify image tags via patches without editing YAML manually. Commit hashes provide audit trails. Avoid storing rendered output in Git; keep only source overlays to maintain single source of truth and prevent merge conflicts.

Yes, via the helmCharts field in kustomization.yaml (standalone kustomize only). This inflates Helm charts during build and allows patching the rendered output using standard Kustomize overlays. Useful when you need Helm's dependency management but want overlay-based customization without maintaining forked charts. Note this feature is unavailable in kubectl-bundled Kustomize and requires careful version pinning to avoid breaking changes between releases.

Over-patching creates fragile overlays that break on base updates. Not using namePrefix causes resource collisions across environments. Storing secrets as plain YAML defeats security benefits. Ignoring generator hashes leads to stale ConfigMaps after content changes. Mixing strategic and JSON patches inconsistently confuses reviewers. Start simple with minimal overlays, validate builds in CI, and document patch rationale to maintain long-term configurability as infrastructure complexity grows.

Run kustomize build with --stacktrace for detailed error context. Validate each overlay incrementally by building base first, then adding patches one at a time. Check indentation and list key identifiers for strategic merge issues. Use kustomize cfg grep to search rendered output. Compare expected versus actual YAML with diff tools. Common failures include missing resource references, incorrect patch targets, and generator misconfiguration. Isolate problems by reducing overlay scope systematically.

Kustomize scales well when teams own distinct bases or component layers. Use shared bases for platform standards and team-specific overlays for application configs. Implement naming conventions and automated linting to prevent cross-team conflicts. For very large estates, consider combining with Config Connector or Crossplane for higher-level abstractions. Kustomize excels at configuration variance but lacks native multi-cluster orchestration, requiring supplementary tooling for global platform governance.

Kustomize has a lower initial barrier than Helm since it uses standard YAML without templating syntax. Developers familiar with kubectl adapt within days. Strategic merge semantics require some Kubernetes schema knowledge. Advanced features like transformers and plugins add complexity gradually. Teams migrating from sed/awk scripts find immediate value. Those deeply invested in Helm may resist switching unless template fatigue is significant. Most engineers reach proficiency in two to three weeks of practical overlay authoring.

Share this article

Quick Contact Options
Choose how you want to connect me: