Skip to content

11.08 Patches in Kustomize

Abstract

Kustomize patches are used when you need targeted changes to specific Kubernetes resources. Unlike common transformers, patches are more precise and are useful for changing fields like replicas, container images, labels, annotations, environment variables, resource limits, or object names.


Why Use Patches?

Common transformers apply changes broadly across many resources.

Patches are used when you want a surgical change on one or more specific resources.

Tip

Use common transformers for global changes like labels, namespace, prefixes, and suffixes. Use patches when only a specific object or field should change.


Patch Basics

A patch usually needs three things:

Part Meaning
Operation What action to perform: add, remove, or replace
Target Which Kubernetes resource should be patched
Value New value to add or replace

Note

The value field is required for add and replace, but not required for remove.


Patch Target Fields

You can target resources using one or more fields.

target:
  kind: Deployment
  name: api-deployment

Common target fields:

Field Purpose
kind Resource type, such as Deployment, Service, or ConfigMap
version / group API version or API group
name Resource name
namespace Resource namespace
labelSelector Select resources using labels
annotationSelector Select resources using annotations

Warning

Be specific in production. A broad patch target can accidentally modify multiple resources.


JSON 6902 Patch

JSON 6902 patches define an operation, a field path, and a value.

Replace Deployment Name

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: replace
        path: /metadata/name
        value: web-deployment

This changes:

metadata:
  name: api-deployment

to:

metadata:
  name: web-deployment

Example

The path /metadata/name means: go to metadata, then update the name field.


Replace Replicas

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5

This changes:

spec:
  replicas: 1

to:

spec:
  replicas: 5

Success

This is a common production use case: keep the same base manifest, but patch replica counts differently for dev, staging, and prod.


Common JSON 6902 Operations

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5
patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: add
        path: /metadata/labels/env
        value: prod
patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: remove
        path: /metadata/labels/debug

Failure

Do not provide value for a remove operation. Only provide the path to remove.


Strategic Merge Patch

Strategic merge patches look like normal Kubernetes YAML. Instead of writing op, path, and value, you write only the fields you want to change.

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

resources:
  - api-depl.yaml

patches:
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: api-deployment
      spec:
        replicas: 5

Note

Strategic merge patches are easier to read because they look like regular Kubernetes manifests.


JSON 6902 vs Strategic Merge Patch

Patch Type Best For Style
JSON 6902 Patch Exact field operations op, path, value
Strategic Merge Patch YAML-style changes Regular Kubernetes YAML

Which one should I use?

Use strategic merge patch when readability matters. Use JSON 6902 patch when you need precise add/remove/replace operations using exact paths.


Inline Patch vs Separate Patch File

You can define patches directly inside kustomization.yaml, or keep them in a separate file.


JSON 6902 Inline Patch

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5

JSON 6902 Separate Patch File

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

resources:
  - api-depl.yaml

patches:
  - path: replica-patch.yaml
    target:
      kind: Deployment
      name: api-deployment
replica-patch.yaml
- op: replace
  path: /spec/replicas
  value: 5

Tip

Use separate patch files when patches become large or when you want cleaner environment-specific overlays.


Strategic Merge Inline Patch

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

resources:
  - api-depl.yaml

patches:
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: api-deployment
      spec:
        replicas: 5

Strategic Merge Separate Patch File

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

resources:
  - api-depl.yaml

patches:
  - path: replica-patch.yaml
replica-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 5

Example

In production overlays, separate patch files are easier to review in Git pull requests.


Example Folder Structure

k8s/
├── base/
│   ├── api-depl.yaml
│   ├── api-service.yaml
│   └── kustomization.yaml
└── overlays/
    ├── dev/
    │   ├── kustomization.yaml
    │   └── replica-patch.yaml
    ├── stg/
    │   ├── kustomization.yaml
    │   └── replica-patch.yaml
    └── prod/
        ├── kustomization.yaml
        └── replica-patch.yaml
overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

patches:
  - path: replica-patch.yaml
overlays/prod/replica-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 5

Build and Apply

kustomize build overlays/prod/
kubectl apply -k overlays/prod/
kustomize build overlays/prod/ | kubectl apply -f -
kubectl delete -k overlays/prod/

Warning

Always preview with kustomize build before applying production changes.


Patching Dictionary Keys

A dictionary in Kubernetes YAML is a key-value map, such as labels, annotations, nodeSelector, or env style maps.

Example base manifest:

api-depl.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: api
  template:
    metadata:
      labels:
        component: api
    spec:
      containers:
        - name: nginx
          image: nginx

Note

In YAML, labels is a dictionary. To update one label, patch the exact key inside the dictionary.


Replace a Dictionary Key Value

JSON 6902: Replace Label Value

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: replace
        path: /spec/template/metadata/labels/component
        value: web

Result:

spec:
  template:
    metadata:
      labels:
        component: web

Example

/spec/template/metadata/labels/component means: go to spec, then template, then metadata, then labels, then update the component key.

Strategic Merge: Replace Label Value

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

resources:
  - api-depl.yaml

patches:
  - path: label-patch.yaml
label-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  template:
    metadata:
      labels:
        component: web

Tip

Strategic merge is easier to read when changing labels because it looks like normal Kubernetes YAML.


Add a New Dictionary Key

JSON 6902: Add Label

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: add
        path: /spec/template/metadata/labels/org
        value: KodeKloud

Result:

spec:
  template:
    metadata:
      labels:
        component: api
        org: KodeKloud

Success

The add operation keeps the existing dictionary keys and adds the new key.

Strategic Merge: Add Label

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

resources:
  - api-depl.yaml

patches:
  - path: label-patch.yaml
label-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  template:
    metadata:
      labels:
        org: KodeKloud

Result:

labels:
  component: api
  org: KodeKloud

Note

Strategic merge combines the patch with the original manifest. Since org does not exist in the original labels dictionary, it gets added.


Remove a Dictionary Key

JSON 6902: Remove Label

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: remove
        path: /spec/template/metadata/labels/org

Result:

labels:
  component: api

Warning

For remove, do not provide value. Only provide the exact path to the key that must be removed.

Strategic Merge: Remove Label

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

resources:
  - api-depl.yaml

patches:
  - path: label-patch.yaml
label-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  template:
    metadata:
      labels:
        org: null

Tip

In strategic merge patches, setting a dictionary key to null removes that key.


Dictionary Patch Comparison

Action JSON 6902 Strategic Merge
Replace key value op: replace with full path to key Set the key to the new value
Add new key op: add with full path to key Add the new key in the patch file
Remove key op: remove with full path to key Set the key to null

When should I use JSON 6902 for dictionaries?

Use JSON 6902 when you want exact control over the key path and operation.

When should I use strategic merge for dictionaries?

Use strategic merge when you want readable YAML that looks close to the original manifest.


Production Notes for Dictionary Patches

Do

  • Use dictionary patches for labels, annotations, selectors, and pod template metadata.
  • Keep label changes consistent between selector.matchLabels and template.metadata.labels when required.
  • Prefer strategic merge for simple label additions.
  • Prefer JSON 6902 when removing a specific key.
  • Validate the rendered output before applying.

Don't

  • Do not change Deployment selectors casually in production.
  • Do not remove labels used by Services, NetworkPolicies, PodDisruptionBudgets, or monitoring selectors.
  • Do not use broad patch targets that affect multiple Deployments unintentionally.
  • Do not forget that pod template labels are under /spec/template/metadata/labels, not /metadata/labels.

Common mistake

Updating only template.metadata.labels may not update selector.matchLabels. If a Service or Deployment selector depends on the old label, traffic routing or rollout behavior can break.


Patching Lists

Lists in Kubernetes YAML are arrays of items. Common examples include:

  • containers
  • initContainers
  • volumes
  • env
  • ports
  • volumeMounts

Example base manifest:

api-depl.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: api
  template:
    metadata:
      labels:
        component: api
    spec:
      containers:
        - name: nginx
          image: nginx

Note

The containers field is a list because it starts with -. A pod can have one container or multiple containers.


Replace a List Item

JSON 6902: Replace First Container

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: replace
        path: /spec/template/spec/containers/0
        value:
          name: haproxy
          image: haproxy

Result:

containers:
  - name: haproxy
    image: haproxy

Example

/spec/template/spec/containers/0 means: go to the containers list and replace the first item.

Note

List indexes start from 0.

  • First container: /containers/0
  • Second container: /containers/1
  • Third container: /containers/2

Strategic Merge: Replace Container Image

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

resources:
  - api-depl.yaml

patches:
  - path: container-patch.yaml
container-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  template:
    spec:
      containers:
        - name: nginx
          image: haproxy

Result:

containers:
  - name: nginx
    image: haproxy

Tip

Strategic merge patches match containers by name. Here, the container named nginx is updated with the new image.


Add a List Item

JSON 6902: Append Container to End of List

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: add
        path: /spec/template/spec/containers/-
        value:
          name: haproxy
          image: haproxy

Result:

containers:
  - name: nginx
    image: nginx
  - name: haproxy
    image: haproxy

Success

In JSON 6902 patches, /- appends the new item to the end of the list.

Tip

You can also add at a specific index.

path: /spec/template/spec/containers/0

This adds the item at the beginning of the list.

Strategic Merge: Add Container

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

resources:
  - api-depl.yaml

patches:
  - path: container-patch.yaml
container-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  template:
    spec:
      containers:
        - name: haproxy
          image: haproxy

Result:

containers:
  - name: nginx
    image: nginx
  - name: haproxy
    image: haproxy

Note

Strategic merge sees that haproxy is a new container name, so it adds it to the container list.


Remove a List Item

Example base manifest with two containers:

api-depl.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: api
  template:
    metadata:
      labels:
        component: api
    spec:
      containers:
        - name: web
          image: nginx
        - name: database
          image: mongo

JSON 6902: Remove Container by Index

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

resources:
  - api-depl.yaml

patches:
  - target:
      kind: Deployment
      name: api-deployment
    patch: |-
      - op: remove
        path: /spec/template/spec/containers/1

Result:

containers:
  - name: web
    image: nginx

Warning

JSON 6902 removes list items by index. If the order changes, the wrong container can be removed.

Strategic Merge: Delete Container by Name

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

resources:
  - api-depl.yaml

patches:
  - path: container-patch.yaml
container-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  template:
    spec:
      containers:
        - name: database
          $patch: delete

Result:

containers:
  - name: web
    image: nginx

Success

Strategic merge can delete a container by name using $patch: delete.


List Patch Comparison

Action JSON 6902 Strategic Merge
Replace item Replace by list index like /containers/0 Match by merge key such as container name
Add item Use op: add and /- to append Add a new item with a new name
Remove item Use op: remove with list index Use $patch: delete with the item name

Which one should I use for lists?

Use strategic merge for container lists when possible because it matches by container name. Use JSON 6902 when you need exact index-based control.


Production Notes for List Patches

Do

  • Prefer strategic merge when patching containers, initContainers, and similar named lists.
  • Use JSON 6902 carefully when the list order is stable and controlled.
  • Validate rendered manifests with kustomize build before applying.
  • Keep container names stable across environments.
  • Review patches during code review because list patches can change runtime behavior quickly.

Don't

  • Do not remove containers by index in production unless you are certain the list order will not change.
  • Do not patch sidecars blindly; service mesh, logging, security, or monitoring sidecars may be required.
  • Do not replace an entire container item if you only need to update one field.
  • Do not forget readiness/liveness probes, ports, env vars, and volume mounts when replacing full container objects.

Common mistake

Replacing /spec/template/spec/containers/0 replaces the entire container object. If the original container had probes, resources, ports, env variables, or volume mounts, they will be lost unless you include them again.


Quick Commands

# Render final YAML
kustomize build .

# Apply rendered YAML
kubectl apply -k .

# Preview only container section after build
kustomize build . | grep -A20 "containers:"

Tip

For production GitOps workflows, always commit both the base manifest and the patch file so the final change is reviewable.


Production Best Practices

Do

  • Keep base manifests clean and reusable.
  • Use overlays for environment-specific patches.
  • Use strategic merge patches for readable YAML-based changes.
  • Use JSON 6902 patches for precise field-level changes.
  • Store larger patches in separate files.
  • Review final output using kustomize build.
  • Keep patch targets specific using kind, name, and namespace.

Don't

  • Do not patch too many unrelated fields in one file.
  • Do not use broad targets unless intentional.
  • Do not apply production overlays without previewing the rendered YAML.
  • Do not duplicate entire manifests only to change one field.
  • Do not patch secrets directly in Git unless they are encrypted or externally managed.

Do's and Don'ts

Do Don't
Patch only what changes per environment Copy full YAML files for every environment
Use kustomize build before apply Apply blindly to production
Keep patch files small and focused Mix unrelated changes in one patch
Use Git review for patches Make manual cluster changes without tracking
Use overlays for dev, stg, and prod Edit base manifests for environment-specific values

Quick Troubleshooting

Patch not applied

Check that the target.name, kind, and namespace match the resource after any prefixes, suffixes, or namespace transformations.

Wrong resource changed

Your target is too broad. Add name, namespace, labelSelector, or annotationSelector.

JSON patch path error

Verify the YAML path. For example, replicas is /spec/replicas, not /replicas.

Separate patch file not detected

Confirm the file path is relative to the kustomization.yaml file that references it.


Summary

Quote

Kustomize patches let you keep one reusable base and apply precise environment-specific changes without duplicating Kubernetes manifests.

Patches are best used when:

  • One field needs to change.
  • One resource needs custom behavior.
  • Environments need different replicas, images, labels, or configuration.
  • You want production-safe changes that are clear, reviewable, and repeatable.