# Kustomize: a base, two overlays, a component and generated ConfigMaps

> How kustomize build assembles manifests: a base that lists resources, overlays that patch it per environment, a reusable component, a configMapGenerator with its hash suffix, image and replica transformers, and the objects that come out.

Interactive graph: https://k8wiz.com/graphs/kustomize-base-overlays-components-generators

Kustomize builds manifests from other manifests, with no templates. A `kustomization.yaml` lists resources and the changes to make to them. An overlay points at a base and adds its own changes on top, so each environment is a short file of differences.

The nodes here are files in a repository, apart from the last three, which are what `kubectl apply -k overlays/production` sends to the cluster.

## Resources

### Kustomization base/kustomization.yaml

you write it, kustomize.config.k8s.io/v1beta1

What every environment shares: the Deployment and Service, common labels and a generated ConfigMap. It builds on its own, which is a good test of a base.

### Deployment base/deployment.yaml

you write it, apps/v1

A plain manifest with sensible defaults. It refers to the ConfigMap by its short name, `shop-config`, and Kustomize rewrites the reference when it adds the hash.

### Component components/metrics/kustomization.yaml

you write it, kustomize.config.k8s.io/v1alpha1

An optional feature that any overlay can switch on. Unlike a base, it patches whatever the overlay has built so far, here by adding a metrics port and scrape annotations.

### Kustomization overlays/staging/kustomization.yaml

you write it, kustomize.config.k8s.io/v1beta1

The smallest overlay: a namespace, a name prefix and an image tag. Everything else comes from the base unchanged.

### Kustomization overlays/production/kustomization.yaml

you write it, kustomize.config.k8s.io/v1beta1

Production's differences: a pinned image tag, three replicas, the metrics component, a patch file for resources and one changed key merged into the base's ConfigMap.

### Patch file overlays/production/resources.yaml

you write it

A strategic merge patch: a partial Deployment with only the fields to change. Kustomize matches it to its target by kind and name, and merges containers by their `name`.

### ConfigMap shop-config-7m2k9t4f8h

made by a build or publishing tool, v1, namespace shop

The merged ConfigMap, named with a hash of its content. Change a value and the name changes, which changes the Deployment's Pod template and rolls the Pods.

### Deployment shop

made by a build or publishing tool, apps/v1, namespace shop

The base Deployment after every change: image `ghcr.io/acme/shop:2.4.1`, three replicas, requests, the scrape annotations, the shared label on selector and template, and `envFrom` pointing at the hashed ConfigMap name.

### Service shop

made by a build or publishing tool, v1, namespace shop

From `base/service.yaml`, with the namespace set and the shared label added to its selector, so it still matches the Pods.

## How the resources connect

- Kustomization base/kustomization.yaml → Deployment base/deployment.yaml: resources (references)
- Kustomization overlays/staging/kustomization.yaml → Kustomization base/kustomization.yaml: resources: ../../base (references)
- Kustomization overlays/production/kustomization.yaml → Kustomization base/kustomization.yaml: resources: ../../base (references)
- Kustomization overlays/production/kustomization.yaml → Component components/metrics/kustomization.yaml: components (references)
- Kustomization overlays/production/kustomization.yaml → Patch file overlays/production/resources.yaml: patches (references)
- Kustomization overlays/production/kustomization.yaml → ConfigMap shop-config-7m2k9t4f8h: generates (creates)
- Kustomization overlays/production/kustomization.yaml → Deployment shop: builds (creates)
- Kustomization overlays/production/kustomization.yaml → Service shop: builds (creates)
- Deployment shop → ConfigMap shop-config-7m2k9t4f8h: envFrom, name rewritten (references)
- Service shop → Deployment shop: selector (selects)

## How it works

1. **The base stands alone.** `kubectl kustomize base` prints a Deployment, a Service and a ConfigMap with a hash suffix. Nothing in the base knows which environments use it.
2. **An overlay starts from the base.** `resources: ../../base` builds the base first and takes its output as the overlay's starting point. Staging then sets a namespace, a prefix and an image tag, in three lines.
3. **Production adds a component and a patch.** The component's JSON patch adds the scrape annotations. The patch file merges resource requests into the container named `shop`. Both act on the objects the base produced.
4. **Transformers set image and replicas.** `images` rewrites every container whose image name is `ghcr.io/acme/shop`, and `replicas` sets the count on the Deployment named `shop`. Neither needs a patch.
5. **The generator merges and hashes.** `behavior: merge` combines the overlay's literals with the base's ConfigMap of the same name. The hash is computed last, and every reference to `shop-config` is rewritten to the final name.
6. **Apply the result.** `kubectl apply -k overlays/production` builds and applies in one step. `kubectl diff -k` shows what would change first. Flux and Argo CD run the same build.

## Common mistakes

- A patch that names an object the build doesn't contain fails with `no matches for Id`. With `namePrefix` in play, patches still target the original name from the base.
- A generator in an overlay with the same name as one in the base needs `behavior: merge` or `replace`. Without it the build fails on the duplicate.
- Each change to a generated ConfigMap leaves the old one behind, since its name differs. Apply with `--prune` and a label, or let Flux or Argo CD prune.
- `images` matches on the image name without the tag. A manifest that writes the registry differently, such as `docker.io/library/nginx` against `nginx`, isn't matched.
- `commonLabels` adds labels to selectors, which are immutable on a Deployment. Adding it to a base that is already deployed breaks the next apply; `labels` with `includeSelectors: false` doesn't.

## The manifests you write

```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
labels:
  - pairs:
      app.kubernetes.io/name: shop
    includeSelectors: true
configMapGenerator:
  - name: shop-config
    literals:
      - LOG_LEVEL=info
      - FEATURE_ORDERS=false
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shop
spec:
  replicas: 1
  selector:
    matchLabels:
      app: shop
  template:
    metadata:
      labels:
        app: shop
    spec:
      containers:
        - name: shop
          image: ghcr.io/acme/shop
          envFrom:
            - configMapRef:
                name: shop-config
---
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
patches:
  - target:
      kind: Deployment
      name: shop
    patch: |-
      - op: add
        path: /spec/template/metadata/annotations
        value:
          prometheus.io/scrape: "true"
          prometheus.io/port: "9090"
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shop-staging
namePrefix: staging-
resources:
  - ../../base
images:
  - name: ghcr.io/acme/shop
    newTag: 2.5.0-rc.1
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shop
resources:
  - ../../base
components:
  - ../../components/metrics
images:
  - name: ghcr.io/acme/shop
    newTag: 2.4.1
replicas:
  - name: shop
    count: 3
patches:
  - path: resources.yaml
configMapGenerator:
  - name: shop-config
    behavior: merge
    literals:
      - FEATURE_ORDERS=true
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shop
spec:
  template:
    spec:
      containers:
        - name: shop
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
```
