Kustomize plugins let you write your own Go code to transform Kubernetes manifests.
Let’s see a custom transformer in action. Imagine you have a standard Kubernetes Deployment manifest, and you want to inject a sidecar container into it. Normally, you’d have to manually edit the YAML, but with a Kustomize plugin, you can automate this.
Here’s a kustomization.yaml that uses a hypothetical add-sidecar plugin:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
transformers:
- |-
apiVersion: internal.config.k8s.io/v1alpha1
kind: Transformer
metadata:
name: add-sidecar-transformer
plugin:
name: add-sidecar # This refers to the Go plugin binary
version: "v1"
config:
container:
name: my-sidecar
image: "my-registry/my-sidecar-image:latest"
ports:
- containerPort: 8080
targetSelector:
kind: Deployment
And here’s the deployment.yaml it operates on:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: main-app
image: nginx:latest
When you run kustomize build ., the add-sidecar plugin would be invoked. It reads the deployment.yaml, finds the Deployment resource, and injects the specified sidecar container into the spec.template.spec.containers list. The output would look something like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: main-app
image: nginx:latest
- name: my-sidecar
image: my-registry/my-sidecar-image:latest
ports:
- containerPort: 8080
This custom transformer mechanism is built around Go plugins. Kustomize dynamically loads these plugins. When Kustomize encounters a Transformer kind in your kustomization.yaml that specifies a plugin.name, it looks for an executable file with that name (e.g., add-sidecar) in a specific directory path. This path is typically $HOME/.config/kustomize/plugin/ or within the Kustomize binary’s own plugin directory.
The Go plugin itself needs to implement a specific interface defined by Kustomize. This interface dictates how the plugin receives configuration (the config block in your kustomization.yaml) and how it returns the modified Kubernetes objects. The plugin reads the input Kubernetes manifests, applies its transformation logic based on the provided config, and then outputs the modified manifests.
The core problem this solves is the need for repeatable, programmatic modifications to Kubernetes manifests that go beyond the basic patching capabilities of Kustomize (like patchesStrategicMerge or patchesJson6902). This is crucial for complex deployments where you might need to inject shared init containers, add specific annotations based on environment variables, or perform other intricate manifest manipulations that would otherwise require manual editing or complex scripting.
You can also build these plugins into a single binary. If you compile your Go plugin and place the executable file named add-sidecar in a directory Kustomize searches (like ~/.config/kustomize/plugin/add-sidecar/v1/add-sidecar), Kustomize will find and execute it directly. The version: "v1" in the kustomization.yaml is important here; it maps to the directory structure v1 within the plugin’s location.
The targetSelector in the plugin configuration is a powerful way to scope your transformer’s actions. Without it, the transformer might attempt to modify all resources of a certain type, which can lead to unintended consequences. By specifying kind: Deployment, you ensure that only Deployment resources are considered for transformation. You can further refine this using name, namespace, or label selectors within the targetSelector.
The most surprising thing about Kustomize alpha plugins is that they are essentially just Go programs that Kustomize executes. Kustomize doesn’t "understand" the plugin’s logic; it simply provides a standardized way to pass input manifests and configuration to the plugin and receive the transformed manifests back. This means you can write incredibly sophisticated transformations using the full power of the Go language and its ecosystem.
The way Kustomize handles plugin execution involves a specific RPC (Remote Procedure Call) mechanism. When Kustomize finds a plugin, it doesn’t just run a command; it spawns the plugin executable as a separate process and communicates with it using a form of RPC over standard input/output. This allows Kustomize to manage the plugin’s lifecycle and exchange data efficiently.
The next challenge you’ll likely encounter is managing the lifecycle and distribution of these custom plugin binaries across your development and deployment environments.