k8wiz
Back to Articles

Ready to test your Kustomize knowledge? Try our Kustomize quiz!

How to replace deprecated Kustomize fields with kustomize edit fix

Clear Kustomize deprecation warnings: what kustomize edit fix rewrites, how to prove the output didn't change, and how to move vars to replacements.

8 min read
By k8wiz Team
  • kustomize
  • migration
  • replacements
  • patches

If your repository has been around since Kustomize v3 or v4, kustomize build probably prints a few lines like these before the YAML.

# Warning: 'bases' is deprecated. Please use 'resources' instead. Run 'kustomize edit fix' to update your Kustomization automatically.
# Warning: 'patchesStrategicMerge' is deprecated. Please use 'patches' instead. Run 'kustomize edit fix' to update your Kustomization automatically.
# Warning: 'commonLabels' is deprecated. Please use 'labels' instead. Run 'kustomize edit fix' to update your Kustomization automatically.

The builds still work, so these warnings tend to sit in CI logs for years. Cleaning them up is low risk if you do it in the right order, and the tool that suggests the fix does most of the work. This guide covers what kustomize edit fix changes, how to check that the rendered manifests stay identical, and how to handle the one field it won't convert unless you ask, vars. The details below come from the Kustomize v5.8 source.

Which fields are deprecated

Kustomize v5 warns about six fields. Each has a replacement that does the same job, usually with more options.

Deprecated fieldUse instead
basesresources
imageTagsimages
patchesStrategicMergepatches
patchesJson6902patches
commonLabelslabels
varsreplacements

Kustomize prints the warnings on stderr, so the YAML on stdout stays valid and kustomize build . | kubectl apply -f - keeps working. That is also why nobody notices them. The warnings start with #, but they never reach the YAML stream.

Step 1: start from a clean tree and save the current output

kustomize edit fix rewrites kustomization.yaml in place, and with the --vars option it also rewrites your resource files. Start with a clean Git working tree so you can review every change with git diff and throw it away if something looks wrong.

Then render every kustomization you plan to touch and keep the output. This is the baseline you compare against at the end.

git status --short
mkdir -p /tmp/kfix
for dir in base overlays/dev overlays/prod; do
  kustomize build "$dir" > "/tmp/kfix/$(echo "$dir" | tr / _).before.yaml"
done

Adjust the list of directories to your repository. Include every overlay, because an overlay renders its bases too, and a change in a base shows up in each overlay's output.

Step 2: run kustomize edit fix in each directory

The command works on the kustomization file in the current directory only. It doesn't follow resources into bases, so run it once per directory.

for dir in base overlays/dev overlays/prod; do
  (cd "$dir" && kustomize edit fix)
done

For each directory it prints a summary of the conversions it handles.

Fixed fields:
  patchesJson6902 -> patches
  patchesStrategicMerge -> patches
  commonLabels -> labels

To convert vars -> replacements, run the command `kustomize edit fix --vars`

The summary is the same fixed text every time. It lists what the command can convert, not what it found in your file, so read the diff to see what actually changed.

Here is what the command does to each field. It moves bases entries to the end of resources and imageTags entries into images. It turns each patchesJson6902 entry into a patches entry with the same target and path. Each patchesStrategicMerge entry becomes a patches entry with path when it names a file, or with an inline patch when it holds YAML. It also fills in apiVersion and kind if your file left them out, and moves a generator's old envSource into envSources.

Step 3: check the commonLabels conversion

commonLabels needs a closer look, because the conversion keeps a behavior many people don't realize they rely on. commonLabels adds its labels to resource metadata and to selectors and pod templates. The newer labels field only touches selectors when you ask it to.

So kustomize edit fix writes the old labels as a labels entry with includeSelectors: true. A file that started like this:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
  app.kubernetes.io/name: shop
resources:
  - deployment.yaml
  - service.yaml

ends up with the same meaning as this:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
labels:
  - pairs:
      app.kubernetes.io/name: shop
    includeSelectors: true

Leave includeSelectors: true in place for labels that are already deployed. The spec.selector of a Deployment can't change after creation, so dropping a label from the selector makes the next kubectl apply fail on every Deployment that has it. If you want new labels that stay out of selectors, add them as a separate labels entry without includeSelectors.

If the file already uses labels and one of the keys also appears in commonLabels, the command stops with an error naming that key. Remove the duplicate from one of the two fields and run it again.

Step 4: compare the output

Render everything again and diff it against the baseline.

for dir in base overlays/dev overlays/prod; do
  name="$(echo "$dir" | tr / _)"
  kustomize build "$dir" > "/tmp/kfix/$name.after.yaml"
  diff -u "/tmp/kfix/$name.before.yaml" "/tmp/kfix/$name.after.yaml" && echo "$dir: no change"
done

kustomize edit fix runs a similar check itself. It builds the directory before and after the rewrite and prints a warning if the output differs or if the fixed file no longer builds. That check only covers the directory where you ran the command, though, and the loop above covers each overlay that builds on it.

An empty diff is what you want. If something changed, the most common cause is patch order. kustomize edit fix appends converted patches after any existing patches entries, all patchesJson6902 entries first and then all patchesStrategicMerge entries. When two patches touch the same field, the later one wins, so reorder the patches list to match the order the old fields were applied in.

Step 5: convert vars to replacements

vars needs the most care, which is why the plain command skips it. A var copies a value from one resource into $(NAME) references in others. Replacements do the same job with explicit field paths instead of text substitution.

Take a var that puts a Service name into a Deployment's environment.

vars:
  - name: DB_HOST
    objref:
      apiVersion: v1
      kind: Service
      name: postgres
    fieldref:
      fieldpath: metadata.name

The Deployment then has value: $(DB_HOST) in its env list. The replacement version reads the same source field and names the target field directly.

replacements:
  - source:
      kind: Service
      name: postgres
      fieldPath: metadata.name
    targets:
      - select:
          kind: Deployment
          name: api
        fieldPaths:
          - spec.template.spec.containers.[name=api].env.[name=DB_HOST].value

The target field still needs a value in the source file, but it can be anything. A placeholder such as value: DB_HOST_PLACEHOLDER makes the intent obvious, and Kustomize overwrites it during the build.

You can let Kustomize do this conversion with kustomize edit fix --vars. It finds every file under resources that contains $(DB_HOST), adds a target for each place it appears and replaces the reference with DB_HOST_PLACEHOLDER. When the reference is only part of a value, such as --db-host=$(DB_HOST), it adds delimiter and index options to the target so only that piece gets replaced. A reference in the middle of a string converts only when the same character sits on both sides of it. For a value like postgres://$(DB_HOST):5432 the command stops with an error saying the var is not delimited. Move the host into its own field or env var first, then convert.

The command prints its own warning before doing this. Converting vars rewrites resource files, and the result may not render the same output. Run it in a clean tree, review the generated replacements block, then repeat the comparison from step 4. For a handful of vars, writing the replacements by hand often gives a shorter, clearer file.

Step 6: keep the warnings from coming back

Once the diff is clean, commit the change and make CI fail if a deprecated field comes back. Because the warnings go to stderr, you can check for them without parsing the YAML.

#!/usr/bin/env bash
set -euo pipefail
status=0
for dir in base overlays/dev overlays/prod; do
  if kustomize build "$dir" 2>&1 >/dev/null | grep -q "is deprecated"; then
    echo "deprecated Kustomize fields in $dir" >&2
    status=1
  fi
done
exit "$status"

The redirection order matters in that if line. 2>&1 >/dev/null sends stderr into the pipe and throws stdout away, so grep only sees the warnings.

If you also build with kubectl apply -k, keep in mind that kubectl ships its own copy of Kustomize, often a version or two behind the standalone CLI. kubectl has embedded Kustomize v5 since Kubernetes 1.27, and every field in this guide works there.

Test yourself

Ready to test your Kustomize knowledge? Try our Kustomize quiz!