Managing multiple Kubernetes clusters from a single Git repository is a powerful way to achieve consistency and scalability.

Imagine you have a deployment that needs to run on three separate clusters: dev, staging, and prod. Instead of managing three independent sets of Kubernetes manifests, you can use Kustomize to generate the correct configuration for each cluster from a single source of truth.

Here’s a simplified example of what your repository structure might look like:

├── base
│   ├── deployment.yaml
│   └── service.yaml
├── overlays
│   ├── dev
│   │   ├── kustomization.yaml
│   │   └── deployment-patch.yaml
│   ├── staging
│   │   ├── kustomization.yaml
│   │   └── replica-count-patch.yaml
│   └── prod
│       ├── kustomization.yaml
│       └── image-tag-patch.yaml
└── kustomization.yaml

The base directory contains the common Kubernetes resources that apply to all environments. The overlays directory then defines specific modifications for each cluster.

Let’s look at the base/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-registry/my-app:latest
        ports:
        - containerPort: 8080

Now, consider the overlays/dev/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base

patchesStrategicMerge:
- deployment-patch.yaml

And overlays/dev/deployment-patch.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  template:
    spec:
      containers:
      - name: my-app
        image: my-registry/my-app:dev

When you run kustomize build overlays/dev, it takes the base resources and applies the deployment-patch.yaml. This results in a deployment for the dev cluster with 2 replicas and using the my-registry/my-app:dev image.

For the staging overlay, you might have:

overlays/staging/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base

patchesStrategicMerge:
- replica-count-patch.yaml

overlays/staging/replica-count-patch.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 5

Running kustomize build overlays/staging would yield a deployment with 5 replicas, still using the latest image from the base.

The prod overlay could then specify a specific image tag:

overlays/prod/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base

patchesStrategicMerge:
- image-tag-patch.yaml

overlays/prod/image-tag-patch.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: my-app
        image: my-registry/my-app:v1.2.3

kustomize build overlays/prod would produce the deployment with the v1.2.3 image tag and the default replica count from the base.

The real power comes when you integrate this with GitOps tools like Argo CD or Flux CD. You point your GitOps controller to this repository, and it monitors changes. When you update an image tag in prod/image-tag-patch.yaml, commit and push, the GitOps tool detects the change and applies the updated configuration to your prod cluster.

The core problem Kustomize multi-cluster solves is drift. Without a unified approach, each cluster’s configuration tends to diverge over time. Teams might deploy different versions of an application, or have slightly varied resource limits, leading to inconsistencies that are hard to track and debug. Kustomize, by enforcing a single source of truth in Git, makes these differences explicit and manageable through overlays.

The patchesStrategicMerge directive is a common way to apply changes, but Kustomize also supports patchesJson6902 for more complex transformations and commonLabels, commonAnnotations, and replicas fields for simpler, direct modifications. You can even define entirely new resources within an overlay that are specific to that environment.

A detail often overlooked is the use of kustomization.yaml in the root of the repository. This can serve as a default overlay or a way to include common patches across all environments before they are further modified by specific overlays.

The next step in managing multi-cluster deployments involves orchestrating rollout strategies across these environments, such as blue/green deployments or canary releases.

Want structured learning?

Take the full Kustomize course →