Kustomize isn’t a templating language, it’s a template-free way to customize Kubernetes manifests.

Let’s say you’ve got a Helm chart that does 90% of what you need, but you want to inject a few specific, non-templated changes into the generated YAML – maybe add a sidecar container, tweak a resource limit, or inject a specific annotation that your Helm values can’t reach. This is where Kustomize shines as a post-processor.

Here’s a common scenario: you’re deploying a service using a Helm chart, and you need to add a Prometheus scrape_config annotation to the service’s deployment.

Helm Chart (simplified templates/deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:

  name: {{ .Release.Name }}-myapp

spec:

  replicas: {{ .Values.replicaCount }}

  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp

        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

        ports:
        - containerPort: 8080

Helm Values (values.yaml):

replicaCount: 1
image:
  repository: nginx
  tag: latest

When you run helm template myrelease ./mychart, you get standard Kubernetes YAML. But how do you add that Prometheus annotation after Helm has done its work?

You can’t directly add it via Helm values without making your Helm chart overly complex or brittle. This is where Kustomize comes in.

The Kustomize Approach:

  1. Generate Helm Output: First, you generate the raw Kubernetes manifests from your Helm chart.

    helm template myrelease ./mychart > base/deployment.yaml
    

    This base/deployment.yaml will contain the output from your Helm chart.

  2. Create a Kustomization Directory: Create a kustomize directory, and within it, a kustomization.yaml file.

    .
    ├── base
    │   └── deployment.yaml
    └── kustomize
        └── kustomization.yaml
    
  3. Define the Kustomization: Your kustomize/kustomization.yaml will reference the Helm-generated deployment.yaml and define your patches.

    kustomize/kustomization.yaml:

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    
    resources:
    - ../base/deployment.yaml
    
    patchesStrategicMerge:
    - deployment-patch.yaml
    
  4. Create the Patch: Now, create the kustomize/deployment-patch.yaml file. This file contains only the changes you want to apply. Kustomize uses strategic merge patching, which is powerful for Kubernetes objects.

    kustomize/deployment-patch.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myrelease-myapp # Must match the name generated by Helm
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
    

    Notice how this patch file is minimal. It only specifies the metadata.annotations you want to add. Kustomize intelligently merges this into the existing Deployment object.

  5. Apply Kustomize: Run kustomize build from within the kustomize directory.

    cd kustomize
    kustomize build .
    

    The output will be the base/deployment.yaml with your annotations merged in.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myrelease-myapp
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080" # This annotation is added
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: myapp
      template:
        metadata:
          labels:
            app: myapp
        spec:
          containers:
          - name: myapp
            image: nginx:latest
            ports:
            - containerPort: 8080
    

Why this works:

Helm’s template command is purely for rendering Go templates into static YAML. Kustomize, on the other hand, takes these static YAML files as input and applies transformations (like strategic merge patching) without needing to re-interpret any templating language. It’s a clean separation of concerns: Helm for chart-level templating and value injection, Kustomize for fine-grained, template-free manifest manipulation.

This pattern is incredibly useful for integrating off-the-shelf Helm charts into environments with specific operational requirements (like custom monitoring annotations, sidecars for logging/tracing, or security-policy additions) that the original chart authors didn’t anticipate.

The next step you’ll likely encounter is managing multiple patches for different environments, which is where Kustomize’s overlays feature becomes essential.

Want structured learning?

Take the full Helm course →