Kubernetes Kustomize Explained: Deploy NGINX Across Dev, QA & Production
Imagine you are managing a Kubernetes application across Development, QA, and Production.
In Development, you need one replica. In QA, you need two replicas. In Production, you need four replicas with a specific image version.
How would you manage these differences?
One option is to maintain separate Deployment and Service YAML files for every environment. But as your application grows, maintaining multiple copies becomes difficult. A change in one environment might be missed in another.
Kustomize solves this problem by allowing us to reuse Kubernetes YAML and customize it for different environments without duplicating the entire configuration.
Let’s understand how it works using a simple NGINX application.
1. What Is Kustomize?
Kustomize is a Kubernetes configuration management tool that customizes existing YAML manifests without requiring a template language.
It is integrated into kubectl, so we can render and apply configurations directly.
Kustomize has two important concepts:
- Base: The common Kubernetes configuration shared across environments.
- Overlay: The environment-specific customizations applied to that base.
Instead of maintaining three copies of the same Deployment, we define it once and customize only the differences.
Official documentation: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
2. Before Kustomize: The Problem
Suppose we deploy NGINX using a Deployment and a Service.
Our environments have different requirements:
SettingDev, QA, Production Replicas 1, 2, 4 ImageNGINX development versionTested versionApproved versionNamespacedevqaprod
Without Kustomize, we might create separate directories:
kubernetes/
├── dev/
│ ├── deployment.yaml
│ └── service.yaml
├── qa/
│ ├── deployment.yaml
│ └── service.yaml
└── prod/
├── deployment.yaml
└── service.yamlThe problem is that the Deployment and Service definitions are repeated.
If we need to change a container setting, we must ensure that every copy is updated correctly.
This creates unnecessary maintenance and increases the risk of configuration drift.
What if we could define the common configuration once and reuse it everywhere?
That’s where Kustomize comes in.
3. Create a Normal NGINX Deployment and Service
Before using Kustomize, let’s understand the Kubernetes resources themselves.
Create a file named deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.28
ports:
- containerPort: 80This Deployment runs one NGINX replica.
Now create service.yaml:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: ClusterIP
selector:
app: nginx
ports:
- port: 80
targetPort: 80The Service provides a stable internal endpoint for the NGINX Pods.
We can deploy these resources directly using:
kubectl apply -f deployment.yaml
kubectl apply -f service.yamlThis works perfectly well for a simple deployment.
But when we need to manage multiple environments, we need a maintainable way to reuse and customize these configurations.
Let’s introduce Kustomize.
4. Create the Kustomize Base
A base contains the common Kubernetes resources that we want to reuse.
Create this directory structure:
kustomize/
└── base/
├── deployment.yaml
├── service.yaml
└── kustomization.yamlMove the NGINX Deployment and Service manifests into the base directory.
Now create base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomizationresources:
- deployment.yaml
- service.yamlThat’s our base.
We have not changed how Kubernetes Deployments or Services work. We have simply told Kustomize which resources belong together.
We can render the base using:
kubectl kustomize kustomize/baseKustomize reads the configuration and generates the combined Kubernetes YAML.
5. Create Environment Overlays
Now comes the useful part.
We want to reuse the same NGINX Deployment and Service across Dev, QA, and Production.
Get Lokeshbabu Nalluri’s stories in your inbox
Join Medium for free to get updates from this writer.
Create this structure:
kustomize/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── dev/
│ └── kustomization.yaml
├── qa/
│ └── kustomization.yaml
└── prod/
└── kustomization.yamlEach overlay references the same base and defines the changes required for its environment.
Development overlay
Create overlays/dev/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomizationresources:
- ../../basenamespace: devreplicas:
- name: nginx
count: 1
QA overlay
Create overlays/qa/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomizationresources:
- ../../basenamespace: qareplicas:
- name: nginx
count: 2
Production overlay
Create overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomizationresources:
- ../../basenamespace: prodreplicas:
- name: nginx
count: 4
Look at what we have achieved.
We defined the Deployment and Service only once.
Each overlay reuses the base but sets its own namespace and replica count.
We do not need separate copies of the Deployment YAML just to change the number of replicas.
6. How Do We Deploy Each Environment?
Before deploying, render the Development configuration:
kubectl kustomize kustomize/overlays/devThe generated Deployment will have one replica and the dev namespace.
Render QA:
kubectl kustomize kustomize/overlays/qaThe generated Deployment will have two replicas and the qa namespace.
Render Production:
kubectl kustomize kustomize/overlays/prodThe generated Deployment will have four replicas and the prod namespace.
Once the output has been reviewed, we can deploy the selected environment.
For Development:
kubectl apply -k kustomize/overlays/devFor QA:
kubectl apply -k kustomize/overlays/qaFor Production:
kubectl apply -k kustomize/overlays/prodThe target cluster must be configured, and the namespaces must exist or be created as part of the deployment process.
We can verify the resources using:
kubectl get deployments -n dev
kubectl get pods -n dev
kubectl get services -n devUse the corresponding namespace to check QA or Production.
Important: The examples above are intended for learning. A production deployment also requires suitable image versions, resource requests and limits, health probes, security controls, monitoring, and an appropriate release process.
7. Why Is This Useful in Production?
Consider a typical application deployment workflow.
A developer changes the application code. The CI pipeline builds and tests a new container image.
After the image is available in the registry, the deployment configuration is updated with the intended image version.
Kustomize renders the configuration for Development. The team reviews and validates the generated manifests before deployment.
Once testing succeeds, the same application release can be promoted to QA and then Production, with only the approved environment-specific differences.
Kustomize also works well with GitOps tools such as Argo CD, which can monitor configuration stored in Git and synchronize it with the desired Kubernetes state.
This approach provides several benefits:
- Less duplication: Common resources are defined once.
- Consistent configuration: Environments share the same base.
- Controlled differences: Overlays show what changes between environments.
- Easier reviews: Teams can review configuration changes in Git.
- Automation-ready: The same structure can be used in CI/CD workflows.
For Production, use approved image versions or immutable digests rather than relying on mutable tags such as latest. Keep secrets out of plaintext YAML committed to Git, and protect production deployments with appropriate permissions and approval controls.
Remember that Kustomize manages configuration; it does not build images, run tests, approve releases, or monitor application health by itself.
8. Final Thoughts
Kustomize helps solve a common Kubernetes challenge: managing the same application across multiple environments without maintaining duplicate copies of every manifest.
With a base and environment-specific overlays, we can reuse the NGINX Deployment and Service while customizing replica counts and namespaces for Dev, QA, and Production.
The same pattern can be extended to larger applications with multiple Deployments, Services, ConfigMaps, and other Kubernetes resources.
The next step is to automate this workflow using GitHub Actions, allowing us to validate configuration changes and deploy approved releases through different environments.
Define the common configuration once. Customize only what changes. Deploy with confidence.






