Kustomize’s namespace transform is a surprisingly powerful tool for dynamically relocating resources between Kubernetes namespaces without modifying your base manifests.

Let’s say you have a kustomization.yaml that looks like this:

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

namespace: staging

And your deployment.yaml defines a Deployment with a specific namespace:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: production # This will be overridden
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app-container
        image: nginx:latest

When you run kustomize build ., the output will show the namespace field in the metadata of both deployment.yaml and service.yaml (if it had one) changed to staging.

$ kustomize build .
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: staging # <-- Changed!
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app-container
        image: nginx:latest
---
apiVersion: v1
kind: Service
metadata:
  name: my-service # Assuming service.yaml contained a Service named my-service
  namespace: staging # <-- Also changed!
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

This transformation is incredibly useful for managing different environments. You can have a single set of base manifests and then use Kustomize to deploy them to dev, staging, or production simply by changing the namespace field in your kustomization.yaml. It’s not just a string replacement; Kustomize intelligently targets the metadata.namespace field within the Kubernetes objects it processes.

The core problem this solves is avoiding environment-specific manifest files for simple namespace changes. Instead of maintaining deployment-dev.yaml, deployment-staging.yaml, and deployment-prod.yaml, you have one deployment.yaml and multiple kustomization.yaml files, each pointing to a different namespace. This significantly reduces duplication and makes managing your deployments across environments much cleaner.

Internally, Kustomize parses your YAML manifests into structured data. When it encounters the namespace field in kustomization.yaml, it iterates through the generated resources and applies that namespace value to the metadata.namespace field of each object. This is a declarative transformation, meaning you declare the desired state (resources in staging namespace), and Kustomize figures out how to achieve it by modifying the manifests.

You can also combine this with other Kustomize features. For instance, you might have a common-labels transformer that adds a label to all resources, and then use the namespace transform to deploy those labeled resources into a specific environment.

Consider a scenario where you have a kustomization.yaml for production and another for staging.

kustomization-prod.yaml:

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

resources:
  - ../base

namespace: production

kustomization-staging.yaml:

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

resources:
  - ../base

namespace: staging

Running kustomize build --kustomization-path kustomization-prod.yaml would produce manifests for the production namespace, while kustomize build --kustomization-path kustomization-staging.yaml would target the staging namespace.

The namespace transformer is applied after any base resources are loaded. This means if your base manifests already specify a namespace, the namespace field in kustomization.yaml will overwrite it. This is a crucial point: the kustomization.yaml level namespace declaration has higher precedence for the metadata.namespace field than the one defined within the resource files themselves.

A common pitfall is forgetting that this transform only affects the metadata.namespace field. It does not automatically update namespace references within the resource definitions themselves, such as Service selectors, ConfigMap or Secret references, or RoleBinding subjects if they are namespaced. For those, you’d need more advanced patching or a namePrefix/newName strategy if you were truly trying to duplicate resources into different namespaces with distinct names. However, for the direct purpose of relocating existing resources, it’s perfect.

The next concept you’ll likely encounter is how to manage multiple distinct resources or configurations for different environments, which often leads to exploring Kustomize’s patches and overlays capabilities.

Want structured learning?

Take the full Kustomize course →