Free lesson · GenAI Agent Engineering

Use Kustomize patches and generators

Apply strategic merge patches to customize resources. Use ConfigMap and Secret generators to auto-hash names and trigger rollouts on config changes.

Course: Kubernetes Essentials for GenAI Engineers · Chapter 9 · Packaging with Helm & Kustomize

Free to read — no subscription required.

Introduction

When you ship the same GenAI service to dev, staging, and prod, the dangerous instinct is to copy the base manifests and hand-edit each environment. Within a sprint you have three drifting versions of the Deployment, a Secret nobody can trace back to a source, and a ConfigMap update that never rolled the pods because the name didn't change. Patches and generators are the Kustomize answer: targeted overlays that modify a single source of truth, and content-hashed ConfigMaps/Secrets that force a rolling update whenever values change. By the end you'll be able to choose between strategic merge and JSON patches, generate ConfigMaps and Secrets that trigger pod rollouts on change, and combine the two in a production overlay.

Key Terminology

  • Strategic merge patch — a partial YAML document merged with the base by apiVersion/kind/metadata.name, using name as the merge key for list items like containers. Matters here because it's the safe default for editing existing fields.
  • JSON patch (RFC 6902) — explicit add/remove/replace operations targeted by JSON Pointer paths. Matters when strategic merge can't express the change (removing a field, indexing into an array).
  • Generator — a Kustomize directive (configMapGenerator, secretGenerator) that produces a resource AND appends a content hash to its name. Matters because the hashed name is what forces pod rollouts when values change.
  • Overlay — a directory containing a kustomization.yaml that references a base and layers patches, generators, and replacements on top. Matters because every environment difference lives here, never in the base.
  • Merge behaviorbehavior: merge on a generator combines literals with a same-named base resource; behavior: replace overwrites it. Matters because the wrong choice silently drops base values or duplicates the resource.

Concepts

Strategic merge vs. JSON patch

Reach for a strategic merge patch first: it identifies the target by apiVersion/kind/metadata.name, only lists the fields you want to change, and uses name as the merge key inside list fields like spec.template.spec.containers. That makes container-resource bumps and replica changes trivial without touching surrounding fields. Switch to a JSON patch only when strategic merge can't express the operation — removing a field, replacing an array element by index, or adding a new annotation key — accepting that JSON Pointer paths like /spec/template/spec/containers/0/resources are positional and break if container order changes. (see Code Walkthrough)

ConfigMap and Secret generators trigger rollouts

A hand-written ConfigMap update does not restart pods; they keep reading the old values until something else rolls them. Kustomize generators solve this by appending a content hash to the resource name (llm-chat-config-7d8h2k4f9c), so any literal or file change produces a new name, and every Deployment referencing it gets a rolling update. Secret generators behave identically and base64-encode the values. Use behavior: merge to layer environment-specific literals onto the base ConfigMap of the same name; without it, you'll get a duplicate. (see Code Walkthrough)

A production overlay composes these primitives

A complete overlay layers namespace, namePrefix, commonAnnotations, images (tag pins), patches (replica/resource changes), and generators on a single resources: [../../base] reference. The base stays untouched and every environment-specific decision is auditable in one kustomization.yaml.

Loading diagram...

Code Walkthrough

This walkthrough combines the three concepts above into a single production overlay: a strategic merge patch that scales replicas and resizes container resources, a JSON patch that adds an annotation strategic merge can't express, and a configMapGenerator + secretGenerator whose hashed names force rolling updates whenever values change.

Code snippetyaml
1# overlays/prod/kustomization.yaml 2apiVersion: kustomize.config.k8s.io/v1beta1 3kind: Kustomization 4 5resources: 6 - ../../base 7 8namespace: production 9namePrefix: prod- 10 11commonAnnotations: 12 managed-by: kustomize 13 14images: 15 - name: gcr.io/my-project/llm-chat-api 16 newTag: v1.2.0 17 - name: gcr.io/my-project/gemini-proxy 18 newTag: v1.2.0 19 20patches: 21 # Strategic merge: bump replicas and container resources. 22 - path: replica-resource-patch.yaml 23 # JSON patch: add an annotation strategic merge can't express by path. 24 - target: 25 kind: Deployment 26 name: llm-chat-api 27 patch: |- 28 - op: add 29 path: /metadata/annotations/environment 30 value: production 31 32configMapGenerator: 33 - name: llm-chat-config 34 behavior: merge 35 literals: 36 - model-name=gemini-1.5-pro 37 - max-tokens=8192 38 - temperature=0.3 39 - log-level=WARNING 40 41secretGenerator: 42 - name: llm-chat-secrets 43 literals: 44 - api-key=your-gemini-api-key 45 type: Opaque
Code snippetyaml
1# overlays/prod/replica-resource-patch.yaml 2apiVersion: apps/v1 3kind: Deployment 4metadata: 5 name: llm-chat-api 6spec: 7 replicas: 3 8 template: 9 spec: 10 containers: 11 - name: chat-api 12 resources: 13 requests: { cpu: 500m, memory: 512Mi } 14 limits: { cpu: "1", memory: 1Gi } 15 - name: gemini-proxy 16 resources: 17 requests: { cpu: 250m, memory: 256Mi } 18 limits: { cpu: 500m, memory: 512Mi }

The strategic merge patch identifies the target Deployment by apiVersion/kind/metadata.name, and inside containers Kustomize merges by the name field — so chat-api and gemini-proxy are updated independently while every other container field is preserved. The inline JSON patch handles the annotation add, which strategic merge can't reach by path. Both generators emit hashed names (prod-llm-chat-config-<hash>, prod-llm-chat-secrets-<hash>); changing any literal produces a new hash, and pods referencing the resource roll automatically.

You'll know it works when kubectl kustomize overlays/prod | grep -E '(replicas:|name: prod-llm-chat-(config|secrets))' shows replicas: 3 and both generated resources with content-hash suffixes, and a subsequent edit to any generator literal changes the suffix in the rendered output.

Do's and Don'ts

Do's

  1. Do start with a strategic merge patch — it's safer than a JSON patch because it merges by name rather than list index, so container reordering won't silently retarget the wrong workload.
  2. Do use configMapGenerator/secretGenerator for any value that varies by environment — the appended content hash is what makes pods actually roll when the value changes.
  3. Do set behavior: merge when overlaying a same-named base ConfigMap — without it you get a duplicate resource and the base values silently win or lose depending on apply order.

Don'ts

  1. Don't reach for JSON patches first — their JSON Pointer paths (/spec/template/spec/containers/0/...) are positional and break the moment the base reorders its container list.
  2. Don't commit real secret literals to kustomization.yaml — generators don't encrypt, they base64-encode; use Sealed Secrets, External Secrets Operator, or --from-env-file on a gitignored file instead.
  3. Don't hand-edit a generated ConfigMap/Secret in the cluster — the next kubectl apply -k will replace it with a freshly hashed copy and your edit is gone.

This lesson is free to read. Its hands-on lab — real code, in a cloud IDE — is part of the GenAI Agent Engineering subscription.

From · cancel anytime

More free lessons in Kubernetes Essentials for GenAI Engineers

All free lessons in GenAI Agent Engineering