Kubernetes Kustomize: Environment-Specific Configuration (2026)

Kubernetes Kustomize environment configuration

1. Kustomize vs Helm: Overlay vs Template Engine

When managing Kubernetes configurations across multiple environments, two tools dominate the landscape: Kustomize and Helm. While both solve the same core problem — avoiding copy-paste duplication of YAML — they take fundamentally different approaches.

Helm uses a template engine: your Kubernetes manifests are Go templates with placeholders, and Helm renders them by injecting values. Kustomize, on the other hand, uses an overlay approach: you write plain, valid Kubernetes YAML as a base, then apply patches and transformations on top. No templating syntax, no curly braces, no rendering step required.

FeatureKustomizeHelm
YAML validityAlways valid YAMLTemplate syntax breaks native YAML tools
Learning curveLow — patch-basedMedium — Go template DSL
Package distributionNo built-in packagingChart repositories, OCI registries
Environment overridesNative (overlays)values.yaml per environment
kubectl integrationBuilt in since kubectl 1.14Separate CLI required
Secret managementBasic generators; use external toolsBasic values; use external tools
RollbackManual (git revert)Built-in helm rollback
Best forIn-house apps, GitOpsThird-party chart distribution
When to choose Kustomize: You own the application code, use GitOps (ArgoCD, Flux), want plain readable YAML in your repo, and need simple dev/staging/prod differentiation without a full package manager.
When to choose Helm: You are distributing software to external users, need complex conditional logic, or want to consume off-the-shelf charts from the community (e.g., nginx-ingress, cert-manager).

In 2026, many teams use both: Helm for third-party dependencies, Kustomize for their own application manifests. Kustomize can even wrap Helm charts via the helmCharts field, giving you the best of both worlds.

See our Kubernetes Helm Guide for a deep dive into the Helm workflow.

2. Project Structure: Base and Overlays

The canonical Kustomize layout separates base manifests (shared across all environments) from overlays (environment-specific customizations). Each directory contains a kustomization.yaml file that acts as the entry point.

my-app/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── configmap.yaml
└── overlays/
    ├── dev/
    │   ├── kustomization.yaml
    │   └── patch-replicas.yaml
    ├── staging/
    │   ├── kustomization.yaml
    │   ├── patch-replicas.yaml
    │   └── patch-resources.yaml
    └── prod/
        ├── kustomization.yaml
        ├── patch-replicas.yaml
        ├── patch-resources.yaml
        └── patch-hpa.yaml

The base/ directory contains your canonical Kubernetes manifests — the same YAML you would apply to any cluster. Each overlay directory references the base and adds only what differs: more replicas in prod, smaller resource limits in dev, different image tags per environment.

This structure maps cleanly to a GitOps repository. Your CI pipeline builds and tags a new image; your CD tool (ArgoCD, Flux) reads the overlay for the target environment and applies it. See our ArgoCD GitOps guide for the full pipeline.

Tip: Keep your base/ manifests runnable on their own. They should represent a minimal but valid deployment — typically with conservative resource requests and a single replica. Overlays only add or change values.

3. Base kustomization.yaml

The base kustomization.yaml lists the resources to include and applies transformations common to all environments such as labels and namespace defaults.

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

resources:
  - deployment.yaml
  - service.yaml
  - configmap.yaml

commonLabels:
  app: my-app
  team: platform
  managed-by: kustomize

namespace: my-app

images:
  - name: my-app
    newName: registry.example.com/my-app
    newTag: latest

Key fields in the base:

  • resources — list of YAML files (or remote URLs / other kustomization directories) to include.
  • commonLabels — labels applied to every resource and selector. Use with care: changing these is a breaking change for existing deployments.
  • namespace — sets the namespace on all namespaced resources.
  • images — override image names and tags without touching the deployment YAML.

The base deployment.yaml uses the plain image name that the images transformer will rewrite:

# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 256Mi

For managing application configuration data, review our article on Kubernetes ConfigMaps and Secrets.

4. Overlay kustomization.yaml

Each overlay's kustomization.yaml references the base and declares its patches. The dev overlay is the simplest — it typically just bumps replicas to 1 and uses a feature-branch image tag.

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

resources:
  - ../../base

namePrefix: dev-

images:
  - name: my-app
    newName: registry.example.com/my-app
    newTag: dev-latest

patches:
  - path: patch-replicas.yaml
    target:
      kind: Deployment
      name: my-app

commonLabels:
  environment: dev
# overlays/dev/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1

The production overlay is more involved — it increases replicas, raises resource limits, and may add an HPA patch:

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

resources:
  - ../../base
  - hpa.yaml

images:
  - name: my-app
    newName: registry.example.com/my-app
    newTag: v2.4.1

patches:
  - path: patch-replicas.yaml
    target:
      kind: Deployment
      name: my-app
  - path: patch-resources.yaml
    target:
      kind: Deployment
      name: my-app

commonLabels:
  environment: prod

5. Strategic Merge Patches

A strategic merge patch is the most natural patching style for Kubernetes users. You write a partial Kubernetes manifest that mirrors the structure of the resource you want to change, and Kustomize merges it intelligently — lists of named items (like containers) are merged by name, not replaced wholesale.

# overlays/prod/patch-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
        - name: my-app
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              cpu: 2000m
              memory: 1Gi

Because Kubernetes understands that containers is a list of named objects, the patch locates the container named my-app and merges only the resources field — it does not replace other container fields like ports or env.

You can patch environment variables the same way:

# overlays/staging/patch-env.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
        - name: my-app
          env:
            - name: APP_ENV
              value: staging
            - name: LOG_LEVEL
              value: debug
            - name: DB_POOL_SIZE
              value: "5"
Strategic merge vs JSON patch: Use strategic merge patches when you need to add or update named list items (containers, volumes, env vars). Use JSON 6902 patches when you need to remove items or target by array index.

6. JSON 6902 Patches

RFC 6902 JSON Patch gives you precise surgical control: add, replace, or remove any field or array element by path. It is especially useful for removing items from arrays or targeting specific array indices.

# overlays/prod/patch-remove-debug.yaml
- op: remove
  path: /spec/template/spec/containers/0/env/2

Reference this in kustomization.yaml using the inline patch syntax (Kustomize v4+):

# overlays/prod/kustomization.yaml (patch section)
patches:
  - target:
      kind: Deployment
      name: my-app
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5
      - op: add
        path: /spec/template/spec/containers/0/env/-
        value:
          name: JAVA_OPTS
          value: "-Xms512m -Xmx2g -XX:+UseG1GC"
      - op: replace
        path: /spec/template/spec/containers/0/readinessProbe/initialDelaySeconds
        value: 30

Common JSON 6902 operations:

  • add — insert a value. Use /- to append to an array.
  • replace — replace an existing value (path must exist).
  • remove — delete a field or array element.
  • move — move a value from one path to another.
  • copy — copy a value to a new path.
Caution with array indices: JSON 6902 patches that reference /containers/0 will break if the containers list order changes. Prefer strategic merge patches for named-list operations; use JSON 6902 only when strategic merge cannot express the change.

7. ConfigMap Generator

Kustomize's ConfigMap generator creates ConfigMaps from files, literals, or .env files — and automatically appends a content hash to the ConfigMap name. This hash-based naming forces a rolling update whenever the config changes, eliminating the "my pods still have the old config" problem.

# base/kustomization.yaml (configMapGenerator section)
configMapGenerator:
  - name: app-config
    literals:
      - APP_NAME=my-app
      - APP_VERSION=2.4.1
      - FEATURE_FLAGS=cache,tracing
  - name: app-properties
    files:
      - configs/application.properties
      - configs/logback.xml
  - name: app-env
    envs:
      - configs/app.env

Kustomize generates a ConfigMap named something like app-config-7bgh9k2m4t. It also automatically updates all Deployment, StatefulSet, and DaemonSet references to use the new hashed name — so your pods roll out with the new config automatically.

To disable hash suffix (e.g., when the ConfigMap is referenced by an external controller), use the options field:

# base/kustomization.yaml
configMapGenerator:
  - name: stable-config
    literals:
      - KEY=value
    options:
      disableNameSuffixHash: true
      labels:
        config-type: stable
Best practice: Use ConfigMap generators for all application configuration. The automatic hash rollout is one of Kustomize's most valuable features — it guarantees configuration changes are always picked up by new pods.

8. Secret Generator

The Secret generator works identically to the ConfigMap generator but produces Secret objects with base64-encoded values. Like ConfigMaps, generated secrets get a hash suffix that triggers rolling updates.

# overlays/prod/kustomization.yaml (secretGenerator section)
secretGenerator:
  - name: db-credentials
    literals:
      - DB_USER=produser
      - DB_PASSWORD=supersecretpassword
    type: Opaque
  - name: tls-certs
    files:
      - tls.crt=certs/prod.crt
      - tls.key=certs/prod.key
    type: kubernetes.io/tls
Security warning: Never commit plaintext secrets to your Git repository — even in a private repo. The Secret generator is appropriate only for non-sensitive values or local development. For production, use an external secrets operator (External Secrets Operator, Sealed Secrets, Vault Agent) that fetches secrets from a secure store and injects them as Kubernetes Secrets at runtime.

See our Kubernetes Security Best Practices and RBAC Security articles for the full secrets management picture.

9. Image Transformer

The image transformer is one of Kustomize's most-used features in CI/CD pipelines. It lets each overlay declare a specific image tag without patching the Deployment YAML directly. Your CI pipeline can even update the tag programmatically using the kustomize edit set image command.

# overlays/prod/kustomization.yaml
images:
  - name: my-app
    newName: registry.example.com/my-app
    newTag: v2.4.1
  - name: sidecar-proxy
    newName: registry.example.com/envoy-proxy
    newTag: v1.29.3
    digest: sha256:abc123def456...

Fields explained:

  • name — the image name to match in any container spec across all resources.
  • newName — optionally replace the registry/repository path.
  • newTag — the tag to use (e.g., a Git SHA, semver tag, or latest).
  • digest — pin to an immutable digest instead of a mutable tag for production security.

In a GitOps pipeline, your CI job updates the overlay's image tag automatically:

# In your CI script (bash)
cd overlays/prod
kustomize edit set image my-app=registry.example.com/my-app:${GIT_SHA}
git commit -am "chore: bump my-app image to ${GIT_SHA}"
git push

ArgoCD or Flux then detects the Git change and applies the updated overlay to the cluster. See our ArgoCD GitOps guide for the complete pipeline setup.

For resource management patterns around image rollouts, see Kubernetes Resource Management.

10. namePrefix and nameSuffix

namePrefix and nameSuffix prepend or append a string to the names of all resources in a kustomization. This is useful when deploying the same application multiple times into the same namespace — for example, a canary deployment alongside the stable version, or deploying per-team instances of a shared service.

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

resources:
  - ../../base

namePrefix: canary-

images:
  - name: my-app
    newName: registry.example.com/my-app
    newTag: v2.5.0-rc1

patches:
  - target:
      kind: Deployment
      name: canary-my-app
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 2

After applying this overlay, your cluster will have both my-app (stable) and canary-my-app deployments, each with their own Service and ConfigMap named accordingly. Kustomize automatically updates all cross-references so that the canary Service selector still matches the canary Deployment pods.

Note: namePrefix and nameSuffix are applied after commonLabels. When writing patches, use the post-prefix name (e.g., canary-my-app, not my-app) in the patch target.

11. Kustomize Components: Reusable Cross-Cutting Overlays

Introduced in Kustomize v3.7, Components solve a common problem: you have a feature (e.g., Prometheus metrics scraping, external secrets, network policies) that you want to enable in some environments but not others. Components let you package that feature as a reusable unit and opt into it per overlay.

# components/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component

patches:
  - patch: |-
      - op: add
        path: /spec/template/metadata/annotations
        value:
          prometheus.io/scrape: "true"
          prometheus.io/port: "8080"
          prometheus.io/path: "/actuator/prometheus"
    target:
      kind: Deployment

resources:
  - servicemonitor.yaml
# components/network-policy/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component

resources:
  - network-policy.yaml

Enable components selectively in each overlay:

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

resources:
  - ../../base

components:
  - ../../components/monitoring
  - ../../components/network-policy

images:
  - name: my-app
    newName: registry.example.com/my-app
    newTag: v2.4.1
# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

# No components in dev — no monitoring or network policy overhead
images:
  - name: my-app
    newName: registry.example.com/my-app
    newTag: dev-latest

This is far cleaner than duplicating patch files across overlays or using conditional logic. For monitoring setup details, see our Kubernetes Monitoring with Prometheus article.

12. kubectl kustomize vs the kustomize CLI

Kustomize is built into kubectl since version 1.14, so most teams use it without installing anything extra. However, the bundled version lags behind the standalone kustomize CLI by several releases. In 2026, kubectl 1.30+ bundles Kustomize v5.x, which supports all features covered in this article.

Key commands:

# Preview what will be applied (dry run — outputs YAML to stdout)
kubectl kustomize overlays/prod

# Apply an overlay directly
kubectl apply -k overlays/prod

# Delete resources defined by an overlay
kubectl delete -k overlays/prod

# Using the standalone kustomize CLI
kustomize build overlays/prod | kubectl apply -f -

# Diff against the live cluster (requires kubectl diff)
kubectl diff -k overlays/prod

# Update image tag in an overlay (standalone CLI only)
cd overlays/prod && kustomize edit set image my-app=registry.example.com/my-app:v2.5.0
Recommendation: Use kubectl apply -k for day-to-day operations and in CI pipelines. Install the standalone kustomize CLI when you need the kustomize edit commands for automated image tag updates.

Install the standalone CLI:

# Linux/macOS
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash

# macOS with Homebrew
brew install kustomize

# Windows with Chocolatey
choco install kustomize

To learn more about core Kubernetes deployment operations, see our Kubernetes Deployments guide.

13. Integrating Kustomize with ArgoCD

ArgoCD has first-class native support for Kustomize. When you point an ArgoCD Application at a directory containing a kustomization.yaml, ArgoCD automatically runs kustomize build and applies the output. No additional configuration is needed.

# argocd-app-prod.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app-prod
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/my-org/my-app-gitops
    targetRevision: main
    path: overlays/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app-prod
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

You can also pass Kustomize-specific options in the ArgoCD Application:

# Advanced Kustomize options in ArgoCD Application
spec:
  source:
    repoURL: https://github.com/my-org/my-app-gitops
    targetRevision: main
    path: overlays/prod
    kustomize:
      version: v5.3.0          # Pin kustomize version
      images:
        - my-app=registry.example.com/my-app:v2.4.1  # Override image at sync time
      commonLabels:
        gitops-managed: "true"
      namePrefix: prod-         # Additional prefix

With automated sync and self-heal enabled, ArgoCD will automatically apply any overlay changes pushed to the Git repository — completing the GitOps loop. Review the full ArgoCD GitOps workflow for multi-cluster and multi-tenant configurations.

14. Kustomize with Helm: helmCharts Field

One of the most powerful features added to Kustomize v4.1+ is the helmCharts field. It lets you pull a Helm chart, render it with your values, and then apply Kustomize patches on top — giving you the full power of Kustomize's overlay system over third-party Helm charts without maintaining a fork.

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

helmCharts:
  - name: ingress-nginx
    repo: https://kubernetes.github.io/ingress-nginx
    version: 4.10.0
    releaseName: ingress-nginx
    namespace: ingress-nginx
    valuesFile: ingress-nginx-values.yaml
  - name: cert-manager
    repo: https://charts.jetstack.io
    version: v1.14.5
    releaseName: cert-manager
    namespace: cert-manager
    valuesInline:
      installCRDs: true
      prometheus:
        enabled: true

patches:
  - target:
      kind: Deployment
      name: ingress-nginx-controller
    patch: |-
      - op: add
        path: /spec/template/spec/nodeSelector
        value:
          role: ingress
Prerequisite: The helmCharts field requires Helm to be installed on the machine running kustomize build. In ArgoCD, enable the Helm plugin by setting spec.source.kustomize.helmCharts and ensuring the ArgoCD repo-server has Helm available.

This hybrid approach is ideal when you want to consume community Helm charts but manage them through a GitOps workflow with Kustomize overlays. See our Kubernetes Helm Guide for Helm chart authoring and Kubernetes Ingress Guide for ingress-nginx configuration details.

Summary — Kustomize core workflow:
  1. Write plain YAML in base/ with a kustomization.yaml listing resources.
  2. Create overlay directories (overlays/dev, overlays/prod) each with their own kustomization.yaml that references ../../base.
  3. Add patches (strategic merge or JSON 6902), image overrides, ConfigMap generators, and components to each overlay.
  4. Apply with kubectl apply -k overlays/prod or point ArgoCD at the overlay path.
  5. Use kustomize edit set image in CI to update image tags and commit — triggering GitOps sync.

For a comprehensive overview of the full Kubernetes ecosystem, see our Complete Kubernetes Guide.

Quick Reference

Apply overlay:
kubectl apply -k overlays/prod

Preview output:
kubectl kustomize overlays/prod

Diff live cluster:
kubectl diff -k overlays/prod

Update image tag:
kustomize edit set image app=reg/app:v1.2