Kustomize environments let you manage configuration for different deployment stages without duplicating your base Kubernetes manifests.
Imagine you’re deploying a web application. You have a Deployment and a Service manifest. That’s your base. Now, for production, you want more replicas, maybe a different ingress class, and a specific resource limit. For staging, you might want fewer replicas but still a different ingress. For development, you want it all to be as lightweight as possible.
Here’s how you’d set that up with Kustomize.
First, your base directory:
# 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: app-container
image: nginx:latest
ports:
- containerPort: 80
# base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
Now, let’s define our environments. Each environment will have its own kustomization.yaml that references the base and applies specific patches or changes.
Development Environment
This is often the closest to the base, maybe just enabling debug logging or a different image tag.
# environments/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- deployment-dev-patch.yaml
# environments/dev/deployment-dev-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app-container
image: nginx:1.21.6 # Specific dev image
To see the output for dev: kustomize build environments/dev
Staging Environment
Here we might increase replicas and change the ingress.
# environments/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- deployment-staging-patch.yaml
- service-staging-patch.yaml
# environments/staging/deployment-staging-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
template:
spec:
containers:
- name: app-container
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "200m"
memory: "128Mi"
# environments/staging/service-staging-patch.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: LoadBalancer # Staging uses a LoadBalancer
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
To see the output for staging: kustomize build environments/staging
Production Environment
This is where you’d typically have the most replicas and stricter resource limits.
# environments/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- deployment-prod-patch.yaml
- service-prod-patch.yaml
# environments/prod/deployment-prod-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 10
template:
spec:
containers:
- name: app-container
image: nginx:latest # Production image, could be a specific tag
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "500m"
memory: "256Mi"
# environments/prod/service-prod-patch.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: LoadBalancer # Production uses a LoadBalancer
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
To see the output for prod: kustomize build environments/prod
The magic here is that kustomize build <environment_dir> will recursively find the kustomization.yaml, load the base resources, and then apply the patches defined in that environment’s kustomization.yaml. This means your core application manifests (base/deployment.yaml, base/service.yaml) are DRY, and the environment-specific overrides are isolated and clear.
A common pattern is to also use commonLabels or commonAnnotations within each kustomization.yaml to ensure consistent labeling across environments, which is crucial for selectors and monitoring.
If you want to add a new configuration file only for production, you’d add it to environments/prod/kustomization.yaml using the resources field, not in the base. For example, adding a ConfigMap for production:
# environments/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- prod-configmap.yaml # New file for prod
patchesStrategicMerge:
- deployment-prod-patch.yaml
- service-prod-patch.yaml
# environments/prod/prod-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prod-app-config
data:
FEATURE_FLAG_X: "true"
This structure allows you to manage complex configurations for multiple environments efficiently, ensuring consistency while providing the necessary flexibility for each stage of your deployment lifecycle.
The next step is to integrate this into your CI/CD pipeline, using kubectl apply -k environments/prod or similar commands.