DEV Community

Amira Abidi
Amira Abidi

Posted on

Kubernetes Configuration Management with Kustomize: Dev vs Prod

Managing multiple Kubernetes environments looks simple at first.

You create a few YAML manifests, deploy them to development, then adapt them for production.

Then reality happens.

Development and production need different replica counts, resource limits, image tags, hostnames, configuration values, and sometimes completely different infrastructure settings.

Copying the manifests for each environment may work for a small project, but it quickly becomes difficult to maintain.

This is where Kustomize becomes useful.

In my LibraryCorner project, I used Kustomize to manage Kubernetes configurations across separate development and production environments while keeping a shared configuration base.


1. The Problem: Kubernetes Manifests Across Environments

Imagine that we have a simple application deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: librarycorner-api
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: api
          image: librarycorner-api:latest
          ports:
            - containerPort: 8000
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

This might be perfectly fine for development.

But production may require:

  • more replicas
  • different resource requests and limits
  • a production image tag
  • different configuration
  • different ingress/hostname settings
  • stricter security settings

One tempting approach is to create:

deployment-dev.yaml
deployment-prod.yaml
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

and maintain both independently.

The problem is duplication.

If the base Deployment changes, we now have to remember to update multiple copies.

That's where Kustomize provides a cleaner approach.


2. What Is Kustomize?

Kustomize is a Kubernetes-native configuration management tool.

Instead of using templates, Kustomize lets us define a common base configuration and then apply environment-specific customizations through overlays.

The general structure looks like this:

k8s/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
│
└── overlays/
    ├── dev/
    │   └── kustomization.yaml
    │
    └── prod/
        └── kustomization.yaml
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

The important concept is:

                 Base
                  │
          ┌───────┴───────┐
          │               │
         Dev             Prod
       Overlay           Overlay
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

The base contains what is common.

The overlays contain what changes.


3. Building the Base

Let's start with the common configuration.

base/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: librarycorner-api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: librarycorner-api
  template:
    metadata:
      labels:
        app: librarycorner-api
    spec:
      containers:
        - name: api
          image: librarycorner-api
          ports:
            - containerPort: 8000
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

base/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: librarycorner-api
spec:
  selector:
    app: librarycorner-api
  ports:
    - port: 80
      targetPort: 8000
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

Then we define the resources in base/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

At this point, the base describes the application without knowing whether it will run in development or production.


4. Creating the Development Overlay

Now let's create:

overlays/dev/kustomization.yaml
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

We reference the base:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

We can then apply development-specific changes.

For example, we might want only one replica:

replicas:
  - name: librarycorner-api
    count: 1
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

We can also modify the container image:

images:
  - name: librarycorner-api
    newName: librarycorner-api
    newTag: dev
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

The resulting development configuration is generated from the base plus these customizations.


5. Creating the Production Overlay

The production overlay follows the same principle.

overlays/prod/kustomization.yaml
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

replicas:
  - name: librarycorner-api
    count: 3

images:
  - name: librarycorner-api
    newName: librarycorner-api
    newTag: prod
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

Now we have:

Base
 │
 ├── Dev → 1 replica + dev image
 │
 └── Prod → 3 replicas + prod image
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

The common Kubernetes configuration remains in one place.


6. Patches for More Specific Changes

Sometimes changing replicas or images isn't enough.

For example, production may require different resource limits.

We can use a patch.

overlays/prod/resources-patch.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: librarycorner-api
spec:
  template:
    spec:
      containers:
        - name: api
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

Then reference the patch from the production overlay:

patches:
  - path: resources-patch.yaml
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

This keeps the production-specific configuration isolated from the shared base.


7. Environment-Specific Configuration

Another common requirement is changing configuration values between environments.

For example:

ENVIRONMENT=dev
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

versus:

ENVIRONMENT=prod
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

Kustomize can generate ConfigMaps from literals or files.

For example:

configMapGenerator:
  - name: librarycorner-config
    literals:
      - ENVIRONMENT=dev
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

The production overlay can define:

configMapGenerator:
  - name: librarycorner-config
    literals:
      - ENVIRONMENT=prod
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

This allows the same application manifest to be reused while keeping environment-specific configuration separate.

For sensitive information, however, I would not put credentials directly into Git-managed manifests. Secrets should be handled through an appropriate secret-management solution.


8. Previewing the Result

One of my favorite aspects of Kustomize is that we can render the final Kubernetes manifests before applying them.

For development:

kubectl kustomize overlays/dev
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

For production:

kubectl kustomize overlays/prod
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

This makes it possible to inspect exactly what Kubernetes will receive.

We can also apply the configuration directly:

kubectl apply -k overlays/dev
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

or:

kubectl apply -k overlays/prod
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

The -k option tells kubectl to use Kustomize.


9. Kustomize in a CI/CD Pipeline

Kustomize becomes particularly useful when combined with CI/CD.

For example, a simplified deployment pipeline can look like:

Git push
   │
   ▼
Build application image
   │
   ▼
Run tests
   │
   ▼
Push image
   │
   ▼
Update Kubernetes configuration
   │
   ▼
Kustomize overlay
   │
   ▼
Deploy to EKS
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

For example, a pipeline could deploy development using:

kubectl apply -k k8s/overlays/dev
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

while production uses:

kubectl apply -k k8s/overlays/prod
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

This makes the Kubernetes configuration part of the application delivery process instead of maintaining deployment settings manually.


10. How I Used It in LibraryCorner

In LibraryCorner, I used separate Kubernetes configurations for development and production.

The goal was not to create two independent Kubernetes applications.

Instead, I wanted:

One common application configuration

plus

Environment-specific differences.

The structure was conceptually:

k8s/
├── base/
│   ├── deployment
│   ├── service
│   ├── config
│   └── kustomization.yaml
│
└── overlays/
    ├── dev/
    │   └── kustomization.yaml
    │
    └── prod/
        ├── kustomization.yaml
        └── patches/
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

This approach helped keep the environments consistent while still allowing production to have different scaling and resource requirements.

It also fits naturally with the infrastructure architecture of the project:

AWS
│
├── Development
│   └── EKS
│       └── Kustomize → Dev configuration
│
└── Production
    └── EKS
        └── Kustomize → Prod configuration
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

11. Kustomize vs Helm

Kustomize and Helm are often compared because both can be used to manage Kubernetes configurations.

The difference is largely in their approach.

Kustomize

  • works directly with Kubernetes YAML
  • uses bases and overlays
  • doesn't require a templating language
  • is integrated into kubectl

Helm

  • uses charts and templates
  • provides packaging and versioning
  • has a larger ecosystem for distributing applications
  • is particularly useful when deploying reusable third-party applications

For my LibraryCorner project, Kustomize was a good fit because I wanted to maintain a relatively small number of environment-specific configurations without introducing a templating layer.


12. What I Learned

The biggest lesson for me was that environment separation is not only an infrastructure problem.

It also exists at the Kubernetes configuration level.

A clean structure such as:

Base + Dev Overlay + Prod Overlay
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

helps avoid configuration drift and duplicated manifests.

It also makes the deployment process easier to reason about:

Same application
      +
Different environment configuration
      =
Predictable deployments
🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung 🏆 Live Soccer TV: amira abidi/kubernetes configuration management with kustomize dev vs prod bk - Jadwal Siaran Langsung

Kustomize is therefore a relatively small tool, but it solves a very practical problem when Kubernetes configurations start becoming more complex.


Conclusion

Kubernetes manifests are easy to write.

Keeping them maintainable across multiple environments is the harder part.

Kustomize provides a simple Kubernetes-native approach:

Base → Overlays → Environment-specific configuration

For projects running across development and production environments, this can significantly reduce duplication while keeping the differences explicit.

In my case, it became an important part of the LibraryCorner deployment architecture on AWS EKS.

And the next question naturally follows:

How do we make sure these Kubernetes workloads are actually healthy and observable once they're running?

That's where monitoring and observability come in — and that's a topic I've already explored with Prometheus and Grafana in LibraryCorner.

Top comments (0)