Kustomize’s JSON Patch is the most underrated tool for managing Kubernetes configurations, allowing you to surgically alter specific fields without rewriting entire manifests.

Let’s see it in action. Imagine you have a base Deployment manifest like this:

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-container
        image: nginx:latest
        ports:
        - containerPort: 80

You want to change the image to nginx:1.25.0 and add a resource limits for CPU. With Kustomize, you’d create a kustomization.yaml and a patch.yaml:

kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
patchesStrategicMerge:
- patch.yaml

patch.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: my-container
        image: nginx:1.25.0
        resources:
          limits:
            cpu: "500m"
            memory: "256Mi"

Running kustomize build . would output:

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-container
        image: nginx:1.25.0
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: "500m"
            memory: "256Mi"

Notice how only the image and resources.limits were added/modified, while the rest of the original Deployment spec remained untouched. This is the power of strategic merging. Kustomize intelligently identifies matching fields based on apiVersion, kind, metadata.name, and metadata.namespace (if present) and applies changes.

The problem Kustomize JSON Patch solves is the fragility of direct manifest manipulation across environments. If you have a base deployment and need to change the image for production but not staging, or add a sidecar only in a specific namespace, manually editing YAML files is error-prone and difficult to automate. Kustomize allows you to define these environment-specific or feature-specific changes as separate patches, applied on top of a common base.

Internally, Kustomize uses a concept called "strategic merge patch" for fields like containers or volumes that are lists of objects. It tries to match elements within these lists by a unique key (like name for containers, or mountPath for volumes) and then merges or adds fields. For simpler fields, it’s a direct overwrite. The patchesStrategicMerge directive in kustomization.yaml tells Kustomize to apply these changes.

If you need more granular control, especially for operations beyond simple merging or overwriting, Kustomize also supports JSON Patch (RFC 6902). This is defined using patchesJson6902 in your kustomization.yaml. JSON Patch uses operations like "add", "remove", "replace", "move", "copy", and "test" to precisely manipulate JSON documents.

Consider this kustomization.yaml using JSON Patch:

kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
patchesJson6902:
- target:
    kind: Deployment
    name: my-app
  path: patch-json.yaml

patch-json.yaml:

- op: replace
  path: /spec/template/spec/containers/0/image
  value: nginx:1.25.0
- op: add
  path: /spec/template/spec/containers/0/resources/limits
  value:
    cpu: "500m"
    memory: "256Mi"

This patch-json.yaml achieves the same result as the patch.yaml in the previous example but uses explicit JSON Patch operations. The target specifies which resource the patch applies to, and path uses JSON Pointers to navigate the document. The op field dictates the action.

The real power of JSON Patch emerges when you need to do more complex modifications. For instance, if you wanted to remove a specific environment variable from a container, strategic merge patching wouldn’t easily allow that. A JSON Patch remove operation is perfect:

# patch-json-remove-env.yaml
- op: remove
  path: /spec/template/spec/containers/0/env/1 # Assuming the second env var is at index 1

Another critical aspect is how Kustomize handles lists. When using patchesStrategicMerge on lists like containers or env, Kustomize tries to match existing items by a key (e.g., name for containers, name for environment variables). If a match is found, it merges the fields from the patch. If no match is found, it appends the new item. This behavior is generally what you want, but it’s important to know that if you have multiple containers with the same name in your base and patch, the behavior can become less predictable. JSON Patch, however, operates on array indices directly, offering absolute precision but requiring you to know the exact position of the element you want to modify or remove.

A subtle but crucial detail when using JSON Patch’s add operation for nested structures is that Kustomize (and the JSON Patch standard) will create the necessary parent path if it doesn’t exist. For example, if resources didn’t exist in the container spec, the add operation for limits would automatically create the resources map before adding limits inside it. This prevents errors where you try to add a key to a non-existent map.

The next step in managing complex Kubernetes configurations is understanding how to combine different Kustomize features like commonLabels, commonAnnotations, and patches with patchesJson6902 for truly dynamic deployments.

Want structured learning?

Take the full Kustomize course →