The most surprising thing about Kubernetes manifest patching is that you can fundamentally alter a running deployment’s configuration without changing its Git repository history, all while maintaining a clear audit trail.
Let’s see this in action. Imagine we have a simple Deployment in Git:
# git/apps/my-app/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:1.21.0
ports:
- containerPort: 80
And we want to upgrade the image to nginx:1.22.0. Instead of directly editing deployment.yaml in Git, which would create a new commit, we can use a Flux Kustomization object to apply a strategic merge patch.
Here’s our Kustomization that includes the patch:
# git/apps/flux-system/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: my-app-patch
namespace: flux-system
spec:
interval: 5m
path: ./apps/my-app # Path to the original manifests
sourceRef:
kind: GitRepository
name: flux-system # Refers to our GitRepository object
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
image: nginx:1.22.0 # The new image
target:
kind: Deployment
name: my-app # Apply this patch only to the Deployment named 'my-app'
When Flux reconciles this Kustomization, it will fetch the Deployment from ./apps/my-app, apply the strategic merge patch defined in the patches section, and then send the result to the Kubernetes API. The Deployment in Git remains nginx:1.21.0, but the running deployment will be updated to nginx:1.22.0.
This works because Flux, leveraging Kustomize’s strategic merge patch capabilities, understands the structure of Kubernetes resources. It doesn’t just do a simple text-based merge. It identifies the Deployment named my-app and specifically targets the spec.template.spec.containers list. It finds the container named my-app within that list and updates its image field. If the container name didn’t match, or if we were patching a different field, the behavior would adapt. This is crucial for complex objects where simply appending or overwriting entire sections could break the resource.
The real power here is in managing environmental differences or gradual rollouts without polluting your primary Git branches. You can have a base set of manifests in Git and then use separate Kustomization objects (potentially in different branches or Git repositories) to apply environment-specific patches. For example, a staging Kustomization might patch the image to nginx:1.22.0-rc, while a production Kustomization patches it to nginx:1.22.0. All of this can be done by adding new Kustomization objects or modifying existing ones, keeping the base manifests pristine.
What most people don’t realize is how strategic merge patches handle list merging. When you patch a list (like containers or volumes), Kustomize uses specific keys to identify elements within that list for merging or replacement. For containers, it’s name; for volumes, it’s name. If an element with a matching key exists in the target, it’s merged. If not, it’s appended. This precise control prevents accidental duplication or deletion of list items, which is a common pitfall with simpler patching strategies.
The next step is to explore how to use JSON patches within Flux for even more granular control over specific fields.