Kustomize version pinning doesn’t just prevent drift; it actively enables reproducible infrastructure by creating a verifiable audit trail of your applied configurations.

Let’s say you’re deploying a microservice using Kustomize. Your kustomization.yaml might look like this:

resources:
- deployment.yaml
- service.yaml

images:
- name: my-registry/my-app
  newTag: v1.2.3

Here, newTag: v1.2.3 pins the my-registry/my-app image to the exact v1.2.3 tag. When you run kustomize build ., the output will contain this specific image tag. If someone later changes v1.2.3 to latest or a different version without a proper review, the next kustomize build will reflect that change, and the subsequent kubectl apply will deploy a different version of your application. Pinning prevents this silent drift.

But Kustomize’s version pinning goes deeper than just image tags. It applies to the base configurations you’re inheriting from. Consider this kustomization.yaml:

resources:
- ../../base/app-config

patchesStrategicMerge:
- patch-deployment.yaml

images:
- name: my-registry/my-app
  newTag: v1.2.3

Here, ../../base/app-config is a local path. While this works for development, it offers no guarantee about which version of app-config is being used. If app-config is a Git repository that is also being versioned, you need to point Kustomize to a specific commit or tag. This is where Kustomize’s remote bases come into play, often managed by tools like kpt or directly referenced in Kustomize’s bases field (though kpt is the more common and robust approach for managing remote Git bases).

When using kpt to manage remote bases, your kustomization.yaml might not directly contain the Git URL. Instead, kpt manages a local copy of the remote package, and your kustomization.yaml points to that local copy. The pinning happens at the kpt level.

For example, imagine your base configuration is in a Git repository at git@github.com:my-org/infra-config.git. You’ve initialized kpt in your project to manage this base:

kpt pkg get git@github.com:my-org/infra-config.git/app-base@v2.1.0 my-app-base

This command fetches the app-base package from the v2.1.0 tag of the infra-config repository and places it in your local my-app-base directory. Your kustomization.yaml would then reference this local directory:

resources:
- ../my-app-base

patchesStrategicMerge:
- patch-deployment.yaml

images:
- name: my-registry/my-app
  newTag: v1.2.3

Now, the kustomization.yaml itself is pinned to a specific version of the base configuration through the kpt management of the my-app-base directory. To update the base configuration, you would explicitly run a kpt pkg update command, ensuring the change is intentional.

The mental model here is that Kustomize applies transformations to a set of resources. Version pinning is the mechanism that ensures the starting point of those transformations, and the values within the transformations themselves (like image tags), are fixed. Without pinning, the input to Kustomize can change arbitrarily between runs, leading to unpredictable deployments.

When you pin an image tag, you’re telling Kustomize: "For this specific image my-registry/my-app, I want you to use exactly the container image tagged v1.2.3. If v1.2.3 doesn’t exist, or if a new image is pushed with that tag later (which shouldn’t happen with proper tagging practices), kustomize build will still resolve to that specific digest if available, or fail if the tag is unresolvable. This prevents accidental upgrades or downgrades.

The surprising truth is that Kustomize’s pinning mechanism, especially when combined with tools like kpt for remote bases, creates an immutable artifact: the output of kustomize build. This output, when generated from pinned inputs, is a consistent representation of your desired state. You can commit this built output to your Git repository as an immutable artifact, and kubectl apply against that artifact will always result in the same deployment. This is a core principle of GitOps.

The most common way people bypass the intent of pinning is by using newTag: latest. This effectively disables version pinning for that image and relies on the registry’s latest tag, which is mutable and can lead to unexpected deployments. Always use specific, immutable tags (like semantic versioning tags or Git commit SHAs for container images) for your newTag values.

The next step after mastering version pinning is understanding how to manage secrets and sensitive configuration data in a version-controlled Kustomize setup.

Want structured learning?

Take the full Kustomize course →