Right, so you’ve got your kustomization.yaml files. They’re great. You’ve mastered patching a service’s name here, adding a common label there. But then you look across your repository and see the same three strategic merge patches, the same configMap generator, the same annoying tweaks copy-pasted across a dozen slightly different environments. Your DRY (Don’t Repeat Yourself) senses are tingling. This is where Kustomize Components come in, and frankly, they’re the feature that transforms Kustomize from a neat trick into a genuinely powerful architecture tool. Think of them as reusable, composable blocks of configuration. You define the common stuff once and then just mix it into your various overlays.

The best part? They’re not some fancy new concept. A Component is just a directory containing a kustomization.yaml file that is specifically designed to be included by other Kustomize files. It’s Kustomize-ception. The magic is in the components field, and it’s delightfully straightforward.

The Anatomy of a Component

Let’s say every single one of your applications needs a Prometheus sidecar for metrics and a standard app.kubernetes.yaml/version label injected. Instead of writing that for every app, you make a component.

First, create a directory structure. Organization is key here, so don’t just dump this in your root folder.

base/
├── my-app/
│   └── kustomization.yaml
components/
├── prometheus-sidecar/
│   └── kustomization.yaml
├── standard-labels/
│   └── kustomization.yaml
environments/
├── dev/
│   └── kustomization.yaml  # will use components
└── prod/
    └── kustomization.yaml  # will also use components

Now, let’s look inside that components/prometheus-sidecar/kustomization.yaml. It looks exactly like any other Kustomize file, because it is one.

# components/prometheus-sidecar/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# This is a patch that adds a Prometheus container to any Deployment
patchesStrategicMerge:
  - add-sidecar.yaml

configMapGenerator:
  - name: prometheus-config
    files:
      - config/prometheus.yaml

# This component also generates a ConfigMap, so it needs its own resources.
resources:
  - service-monitor.yaml

And the corresponding patch, add-sidecar.yaml:

# components/prometheus-sidecar/add-sidecar.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: "*"  # We use a wildcard because we want this to apply to ANY Deployment
spec:
  template:
    spec:
      containers:
      - name: prometheus-sidecar
        image: prom/prometheus:latest
        args:
          - --config.file=/etc/config/prometheus.yaml
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
      - name: config-volume
        configMap:
          name: prometheus-config

Consuming Components in Your Overlays

Now, for the payoff. Your environment-specific Kustomization (your overlay) can consume this component. It’s beautifully simple. You just point to the component’s path.

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

resources:
  - ../../base/my-app  # This is your base application

# Here's the new magic sauce:
components:
  - ../../components/prometheus-sidecar
  - ../../components/standard-labels  # You can include multiple components!

# And you can still do your usual overlay stuff! It all stacks.
patches:
  - path: dev-env-overrides.yaml
configMapGenerator:
  - name: my-app-config
    behavior: merge
    literals:
      - ENVIRONMENT=dev

When you run kustomize build environments/dev/, it first loads your base app, then layers on everything from the prometheus-sidecar component (its resources, patches, configMaps), then everything from the standard-labels component, and finally applies your dev-specific patches and configMap merges. The order of operations is crucial and, thankfully, intuitive.

Why This Beats Old-School Templating

This is the core insight. You’re not using a templating language that generates brittle YAML with hidden logic. You’re using pure, declarative YAML all the way down. The relationship is clear and explicit in the kustomization.yaml file. If you need to update the Prometheus configuration for every app in every environment, you change one file in the component. kustomize build across your entire repo will instantly reflect that change. This is a maintainability superpower.

Common Pitfalls and Sharp Edges

  1. Path Hell: You’ll notice we use relative paths like ../../components/.... This is both a feature and a curse. It means your directory structure is set in stone. If you move your environments/dev/ directory, you break the paths. There’s no variable interpolation or magical absolute paths here. Plan your repository layout carefully first. This is the price of purity.
  2. Overlap and Overrides: Components are not isolated. A patch in one component can easily interfere with a patch in your base or another component. There’s no namespacing. You have to be mindful of what you’re changing. If two components both try to patch the same container name in a Deployment, you’re going to have a bad time. The best practice is to make components as granular and single-purpose as possible (like our standard-labels one) to minimize these collisions.
  3. It’s Still Kustomize: Remember, a Component is just a Kustomization. Everything you know about resource ordering, patchesStrategicMerge vs. patchesJson6902, and generator behavior still applies. The complexity is the same, it’s just now neatly packaged.

So, embrace components. Use them to encapsulate cross-cutting concerns: security policies, service meshes, logging sidecars, standard labels and annotations. It turns your infrastructure from a pile of copy-pasted YAML into a composed, manageable system. And honestly, that’s the whole point of using this tool in the first place.