Skip to main content
Kubernetes is promoting KYAML, a strict subset of YAML that any parser accepts. It kills type coercion and indentation bugs without a migration.

KYAML: Kubernetes YAML Without the Norway Problem

KU
Kiril Urbonas
5 days ago • 6 min read•1 view

Kubernetes is promoting KYAML, a strict subset of YAML that any parser accepts. It kills type coercion and indentation bugs without a migration.

Key takeaways

  • Kubernetes is promoting KYAML, a strict subset of YAML that any parser accepts.
  • It kills type coercion and indentation bugs without a migration.

KYAML is worth adopting for generated and reviewed manifests, and the reason is not aesthetics. It is a strict subset of YAML with braces, brackets, trailing commas and always-quoted strings, so an unquoted 1.10 or NO can no longer be reinterpreted by a parser, and there is no indentation left to get wrong. Because every KYAML file is valid YAML, adopting it costs you no tooling migration. It will not become your default format, and it will not validate your schema, but as a low-risk hardening step it is hard to argue against.

The bug KYAML exists to kill#

YAML lets you write a string without quotes, and then guesses its type. That is how the "Norway problem" got its name: the country code NO parses as boolean false in YAML 1.1 parsers. The same guessing turns 1.10 into the float 1.1, and a value like 0755 into an octal number. None of these fail loudly. The manifest applies, and the wrong value ends up in your cluster.

Here is a ConfigMap most people would write without a second thought:

yaml.yaml
# a/cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: flags
data:
  country: NO
  version: 1.10

We ran this through kubectl kustomize (kubectl 1.36.1, Kustomize 5.8.1) and the rendered output contained version: 1.1. The trailing zero was gone, and nothing warned us. Your tool chain may handle NO more kindly than a YAML 1.1 parser does, but you should not have to know which one you have. If your app reads version and expects 1.10, this is a production incident that starts as a clean diff.

What KYAML actually changes#

Per the reference at kubernetes.io/docs/reference/encodings/kyaml/ and KEP-5295, KYAML is flow style only: {} for objects, [] for lists, and every string value double-quoted. Lists and maps carry trailing commas so a diff touches one line when you append. Comments survive, which is the reason we prefer it to converting everything to JSON. Keys stay unquoted unless they are ambiguous, such as a key spelled no or yes.

The same ConfigMap as KYAML:

yaml.yaml
# b/cm.yaml
---
{
  apiVersion: "v1",
  kind: "ConfigMap",
  metadata: {
    name: "flags",
  },
  data: {
    country: "NO",
    version: "1.10",
  },
}

That is real output from kubectl create configmap flags --from-literal=country=NO --from-literal=version=1.10 --dry-run=client -o kyaml. The types are no longer a guess, because a quoted string is a string in every parser. Whitespace stops carrying meaning too, so a bad merge or a Helm nindent slip cannot change the structure of the document.

Getting KYAML out of what you already have#

The documented path is the output format. kubectl get configmap flags -o kyaml prints any live object as KYAML, and --dry-run=client -o kyaml does the same for objects you generate. For files already in Git, the Kubernetes blog points to two yamlfmt tools, one from kubernetes-sigs and one from Google (v0.21.0 and later), that convert YAML to KYAML. We have not run those, so read their own docs for flags rather than trusting a guess from us.

On versions: KYAML was alpha in Kubernetes 1.34 behind KUBECTL_KYAML=true, and beta and on by default from 1.35. Our local kubectl 1.36.1 lists kyaml among the -o formats with no environment variable set. If you are pinned to a 1.34 client, export the variable first.

Prove nothing changed before you commit#

Do not take "valid YAML" on faith when you convert a file. Compare what your tools actually parse. Kustomize gives us a cheap, cluster-free way to do it, because it renders both files through the same parser:

bash.bash
$ for d in a b; do printf 'resources:\n  - cm.yaml\n' > $d/kustomization.yaml; done
$ diff <(kubectl kustomize a) <(kubectl kustomize b)
4c4
<   version: 1.1
---
>   version: "1.10"

That diff is the point. A conversion that is truly behavior-preserving produces an empty diff, and one that fixes a latent coercion bug produces exactly the lines you need to review. Run it on every converted manifest in a PR. For Helm output, pipe helm template into a directory with a kustomization.yaml and diff the same way, which fits how Helm and Kustomize already compose.

One caution: KYAML does not rescue a Helm template that emits broken YAML in the first place. It is a serialization style for objects, not a fix for the templating step in front of them.

What not to expect#

KYAML is not the default output. kubectl get -o yaml still prints conventional YAML, and InfoQ's September 2026 coverage says plainly that teams can keep using it and adopt KYAML selectively. It also has no schema awareness. A KYAML file with replicas: "3" is perfectly legal KYAML and will still be rejected or misread by the API server, so you still need kubectl apply --dry-run=server or a validator in CI. Finally, a file that mixes conventions is no worse than before, but you only get the benefit once you commit to the format for a given directory. The release notes for Kubernetes 1.37 are a reminder of how fast the surrounding tooling moves, so pin your client version when you rely on any of this.

The decision, concretely#

  • Do you generate manifests with scripts or kubectl and commit the output? Emit KYAML with -o kyaml: stable diffs, no coercion, no indentation drift.
  • Do you hand-write and hand-review base manifests? Leave them alone until a coercion bug bites, then quote the offending value and move on.
  • Are you converting an existing repo? Convert one directory, run the rendered diff above, and merge only if the parsed output matches or the differences are bugs you wanted fixed.
  • Do you need schema safety? KYAML does not give it to you. Keep server-side dry-run or a schema validator in CI.

The call we'd make#

Use KYAML for anything a machine writes and a human reviews, which is exactly where silent type changes hurt most, and gate every conversion with a rendered diff. Keep hand-authored YAML if your team is happy with it, because nothing forces you to move and the two coexist cleanly. Pin your kubectl version so -o kyaml behaves the same on every laptop and runner.

Explore topics:KubernetesDevOps
React

Get the DevOps Troubleshooting Cheat Sheet

Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.

Share this post
KU

About Kiril Urbonas

DevOps Engineer

549 articles
View all articles by Kiril Urbonas

You might have missed

Evergreen posts worth revisiting.