Right, so you’ve got your pristine, beautifully structured YAML files. And now someone—maybe even future you—wants to make a “tiny” change. You could just copy-paste the whole manifest and tweak one field, but congratulations, you’ve just invented a maintenance nightmare. You now have two sources of truth, and they will inevitably drift. This is why we have patches. Kustomize gives you two primary ways to do this: Strategic Merge Patches (SMPs) and JSON Patches. They are fundamentally different tools for different jobs, and using the wrong one will make you question your life choices. Let’s fix that.

Strategic Merge Patches: The Overlay Artist

Think of an SMP as a transparent overlay. You’re not writing instructions on how to change the base file; you’re just writing a partial YAML file that gets layered on top of it. Kustomize’s job is to intelligently merge the two. The magic, and the frustration, lies in that word “strategic.” It’s not a dumb merge; it uses special, schema-aware logic based on the kind of resource you’re patching.

The most important thing to understand is the $patch directive. This is your scalpel. Let’s say you have a Deployment that you want to add an environment variable to.

base/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: app
        image: my-app:1.0.0
        env:
        - name: LOG_LEVEL
          value: INFO

Now, for your dev overlay, you want to change the image tag and add a new env var. Your patches/strategy.yaml would look like this:

overlays/dev/patches/strategy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app # This is the crucial lookup key
spec:
  template:
    spec:
      containers:
      - name: app
        image: my-app:latest # This will replace the base value
        env:
        - name: NODE_ENV
          value: development # This will be added to the existing env list

You’d reference this in your kustomization.yaml with patchesStrategicMerge:. When Kustomize applies this, it sees the matching apiVersion, kind, and name, and starts merging. The image field is a simple string, so it’s replaced. The env field is a list, so the new entry is appended. This is the “strategic” part.

But what if you need to remove something? This is where the $patch: delete directive comes in. It’s a bit of a hack, frankly, but it works. Let’s say you want to remove that LOG_LEVEL env var entirely in dev.

overlays/dev/patches/delete.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: app
        env:
        - name: LOG_LEVEL
          $patch: delete

This doesn’t set the value to an empty string; it surgically removes the entire entry from the list. This is incredibly powerful, but also a common pitfall. The syntax is unintuitive and easy to forget.

JSON Patches: The Precise Surgeon

If SMPs are a scalpel, JSON Patches (RFC 6902) are a laser. You’re no longer providing a partial resource; you’re providing an explicit set of operations—“add,” “remove,” “replace,” “move,” “copy”—to apply to a specific target. This is both more powerful and more verbose. You trade the readability of SMP for absolute precision.

A JSON Patch is a list of operations. Here’s how you’d do the same changes as before (change the image and add an env var) using a JSON Patch. You define this in your kustomization.yaml under the patches: section with a target: selector.

overlays/dev/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patches:
- target:
    kind: Deployment
    name: my-app
  patch: |-
    - op: replace
      path: /spec/template/spec/containers/0/image
      value: my-app:latest
    - op: add
      path: /spec/template/spec/containers/0/env/-
      value:
        name: NODE_ENV
        value: development

See the difference? No guessing at merge logic. We’re directly saying: “Replace the value at this exact path. Then, add this new value to the end of the env array (that’s what the /- means).”

The power, and the complexity, is in the path. Need to change the third element in the fifth container’s env array? A JSON Patch can do that. An SMP would be a nightmare. The downside? It’s brittle. If the base manifest changes the order of its containers, your patch targeting containers/0 might suddenly hit the wrong one. It’s tightly coupled to the structure.

When to Use Which (The Real-World Guide)

Use Strategic Merge Patches when:

  • Your patch feels like a partial YAML file. You’re mostly adding or updating fields.
  • You’re patching a list and want to rely on Kustomize’s strategic logic for merging named elements (e.g., patching a specific container by its name rather than its position in the array).
  • You need to delete specific entries from a list.

Use JSON Patches when:

  • You need to perform a removal that $patch: delete can’t handle (like removing a specific annotation).
  • You need to operate on a very specific, deeply nested path that SMPs struggle with.
  • You need operations beyond add/replace/delete, like move or copy.
  • The structure of your base is stable and you need unambiguous control.

The biggest gotcha? Mixing them horribly. You can use both in one Kustomization, but be aware of the order of operations. Kustomize applies all SMPs first, then all JSON Patches. If you try to delete something with a JSON Patch and then recreate it with an SMP, you’re going to have a bad time. My advice? Be consistent within a single overlay. Pick one style and stick with it for clarity. SMPs are generally more readable for most day-to-day tasks, but keep JSON Patches in your back pocket for when you need to perform that precise, surgical strike that only a laser can handle.