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.
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
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:
to:
Example
The path /metadata/name means: go to metadata, then update the name field.
Replace Replicas
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:
to:
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
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.
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
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
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: replica-patch.yaml
target:
kind: Deployment
name: api-deployment
Tip
Use separate patch files when patches become large or when you want cleaner environment-specific overlays.
Strategic Merge Inline Patch
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
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: 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
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: replica-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 5
Build and Apply
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:
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
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:
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
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: 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
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:
Success
The add operation keeps the existing dictionary keys and adds the new key.
Strategic Merge: Add Label
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: label-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
template:
metadata:
labels:
org: KodeKloud
Result:
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
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:
Warning
For remove, do not provide value. Only provide the exact path to the key that must be removed.
Strategic Merge: Remove Label
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: 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.matchLabelsandtemplate.metadata.labelswhen 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:
containersinitContainersvolumesenvportsvolumeMounts
Example base manifest:
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
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:
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
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: container-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
template:
spec:
containers:
- name: nginx
image: haproxy
Result:
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
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:
Success
In JSON 6902 patches, /- appends the new item to the end of the list.
Tip
You can also add at a specific index.
This adds the item at the beginning of the list.
Strategic Merge: Add Container
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: container-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
template:
spec:
containers:
- name: haproxy
image: haproxy
Result:
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:
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
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:
Warning
JSON 6902 removes list items by index. If the order changes, the wrong container can be removed.
Strategic Merge: Delete Container by Name
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- api-depl.yaml
patches:
- path: container-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
template:
spec:
containers:
- name: database
$patch: delete
Result:
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 buildbefore 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, andnamespace.
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.