Kustomize’s list merging strategy can be surprisingly non-intuitive, often leading to unexpected overrides rather than additions when you expect the latter.
Let’s see it in action with a simple example. Imagine you have a base Kubernetes manifest with a command list:
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: main
image: nginx:latest
command:
- "/bin/sh"
- "-c"
- "echo 'Hello from base'"
Now, you want to add a new command to this list in an overlay. You might expect Kustomize to append your new command.
# overlay/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: main
command:
- "echo 'Hello from overlay'"
If you run kustomize build overlay, you might be surprised by the output:
# overlay/kustomization.yaml
resources:
- ../base
patchesStrategicMerge:
- deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: main
command:
- "echo 'Hello from overlay'"
Notice that the original command from the base is gone. Kustomize, by default, performs a strategic merge on lists. For lists where elements can be uniquely identified (like commands or arguments, which are often string literals), it tries to match them and then replace or merge based on specific rules. In this case, because the command is a list of strings and there’s no explicit identifier for each element, Kustomize defaults to replacing the entire list.
The problem Kustomize’s list merging solves is managing variations of Kubernetes configurations across different environments (dev, staging, prod) or for different application versions without having to duplicate entire resource definitions. It allows you to "patch" a base configuration with environment-specific or version-specific changes. This is crucial for maintaining consistency and reducing boilerplate in your Kubernetes manifests.
Internally, Kustomize uses a strategy that’s more sophisticated than a simple JSON merge patch. For lists, it attempts to identify elements that should be merged or replaced. If an element has a key that can be used for identification (like name in a ConfigMap data section, or name for a container), Kustomize can merge properties of matching elements. For lists where elements don’t have such identifying keys, or when the keys aren’t present in the patch, Kustomize often behaves as if it’s replacing the entire list. This is why simple string lists like command or args can be entirely overwritten if you’re not careful.
To achieve additive behavior for lists like command or args, you need to explicitly tell Kustomize how to merge. This is done using a kustomization.yaml file in your overlay that defines a patchesStrategicMerge or patchesJson6902 and, crucially, specifies how lists should be handled. For lists of simple strings, Kustomize doesn’t have a built-in "append" operation for patchesStrategicMerge that works out-of-the-box without a bit of a workaround. The most common and robust way to handle additive list merging for things like command or args is to use patchesJson6902. This allows for more granular control over list manipulation, including appending.
Here’s how you’d append to the command list using patchesJson6902:
First, create a kustomization.yaml in your overlay:
# overlay/kustomization.yaml
resources:
- ../base
patchesJson6902:
- target:
kind: Deployment
name: my-app
patch: |-
- op: add
path: /spec/template/spec/containers/0/command/-
value:
- "echo 'Hello from overlay'"
The patch here uses JSON Patch syntax. The op: add with path: /spec/template/spec/containers/0/command/- is the key. It targets the command list within the first container (containers/0) and the trailing - in the path signifies appending to that list.
When you run kustomize build overlay with this configuration, the output will be:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: main
image: nginx:latest
command:
- "/bin/sh"
- "-c"
- "echo 'Hello from base'"
- "echo 'Hello from overlay'"
This demonstrates the additive behavior you were likely expecting. The original command is preserved, and the new command is appended.
The patchesStrategicMerge approach, while simpler for many cases, relies on Kustomize’s heuristics for identifying list elements. When those heuristics don’t align with simple string lists, or when you need precise control, patchesJson6902 becomes indispensable. Understanding the difference between how patchesStrategicMerge and patchesJson6902 handle list operations is critical for predictable Kustomize behavior.
The next common hurdle you’ll encounter is managing multiple containers within the same spec.template.spec.containers list, where you need to add or modify specific containers, not just the first one.