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.