Kustomize’s commonLabels and commonAnnotations are the secret sauce for DRY Kubernetes manifests, letting you push shared configurations down to multiple environments without duplicating them.

Here’s Kustomize in action, managing labels across different deployment environments:

Imagine you have a base Kubernetes configuration, and you want to apply it to development, staging, and production environments. Each environment needs a specific label indicating its deployment stage, but they also all need a common label for internal tracking.

Let’s say your base directory looks like this:

base/
  deployment.yaml
  service.yaml

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: main
        image: nginx:latest

Now, you create overlays for each environment.

overlays/development/kustomization.yaml:

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

commonLabels:
  environment: development
  team: ops

patchesStrategicMerge:
- deployment-patch.yaml

overlays/development/deployment-patch.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2 # Dev gets 2 replicas

overlays/staging/kustomization.yaml:

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

commonLabels:
  environment: staging
  team: ops

patchesStrategicMerge:
- deployment-patch.yaml

overlays/staging/deployment-patch.yaml:

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

overlays/production/kustomization.yaml:

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

commonLabels:
  environment: production
  team: ops

patchesStrategicMerge:
- deployment-patch.yaml

overlays/production/deployment-patch.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 10 # Production gets 10 replicas

When you run kustomize build overlays/development, you’ll get:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    environment: development # Applied from commonLabels
    team: ops             # Applied from commonLabels
  name: my-app
spec:
  replicas: 2 # Overridden by patch
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - image: nginx:latest
        name: main
---
apiVersion: v1
kind: Service
metadata:
  name: my-service # Assuming you had a service in base
spec:
  selector:
    app: my-app

Notice how environment: development and team: ops were automatically added to the Deployment’s metadata. The replicas count was also updated by the patch. This happens for all resources defined in the base and referenced by the kustomization.yaml.

The commonLabels and commonAnnotations fields in kustomization.yaml are powerful. They act as a global transformer, injecting the specified key-value pairs into the metadata of every resource that Kustomize processes from the resources or bases fields, and their descendants. This means you don’t have to manually add environment: production to every single Deployment, StatefulSet, or ConfigMap in your production overlay. Kustomize does it for you.

The primary problem this solves is configuration duplication and drift. Without commonLabels and commonAnnotations, you’d end up with identical environment or team labels scattered across dozens or hundreds of YAML files in different overlay directories. When a team name changes, or you need to add a new tracking label, you’d have to update it everywhere, which is error-prone and tedious. Kustomize centralizes this.

Internally, Kustomize builds an in-memory representation of your Kubernetes resources. Before it outputs the final YAML, it iterates through all the loaded resources and applies the commonLabels and commonAnnotations to their metadata sections. If a label or annotation already exists on a resource, the commonLabels or commonAnnotations will overwrite it. This is why the patchesStrategicMerge for replicas works independently – labels and annotations are merged differently from other fields like spec.replicas.

The commonLabels and commonAnnotations are applied after resources are loaded from resources or bases and before patches are applied. This means that if you define a label in commonLabels and then try to remove it or change it in a patchesStrategicMerge or patchesJson6902, the commonLabels value will be the one that ultimately takes effect for labels. For annotations, it’s similar. This ordering is crucial to understand when debugging.

The next concept you’ll likely grapple with is managing secrets and sensitive data across environments, often involving tools like sealed-secrets or external secret operators.

Want structured learning?

Take the full Kustomize course →