Skip to main content
BlogDevOps

KYAML in Kubernetes 1.37: Stable YAML Subset, kubectl -o kyaml

KYAML went Stable in Kubernetes v1.37. What the format enforces, which kubectl versions support it, and how to use kubectl get -o kyaml and yamlfmt in CI diffs.

Matthews Wong

Software Engineer

October 202611 min read
KYAML in Kubernetes 1.37: Stable YAML Subset, kubectl -o kyaml

Key Takeaway

KYAML is a strict, flow-style subset of YAML that Kubernetes SIG CLI graduated to Stable in v1.37. Every KYAML file is valid YAML, so existing manifests need no change. kubectl get -o kyaml emits double-quoted strings, braces and brackets, which makes diffs whitespace-proof and ends silent type coercion such as the Norway bug.

The failure KYAML targets is quiet. A wrongly indented manifest can stay valid YAML while describing a different object, and an unquoted value can be read as another type without any error. Nothing fails, and in review the change looks like whitespace noise. The Kubernetes project has now put a stricter answer into kubectl and called it Stable.

This post covers what the Kubernetes project actually shipped when KYAML graduated to Stable in v1.37, on 26 August 2026. I read the v1.37 release notes, the SIG CLI blog post on pretty-printing, the KEP 5295 text, the kuberc reference and the yamlfmt documentation. I have not benchmarked anything. Where a command below is my own composition from documented pieces, I say so.

What graduated to Stable, and what did not change

The v1.37 release post describes KYAML as a safer and less ambiguous subset of YAML designed specifically for Kubernetes, and it is explicit that it is not a replacement for YAML. Every KYAML file is valid YAML, so it is valid input for any version of kubectl, and spec files do not need to be written in KYAML to be parsed. The post states that existing manifests, tooling and pipelines do not need to change.

The stage history is Alpha in v1.34, Beta in v1.35 and Stable in v1.37, with conformance testing complete. The release notes put it in the group of 16 enhancements promoted to Stable in that release. The concrete, user-facing result is one sentence: kubectl get -o kyaml is now Stable. KEP 5295, owned by SIG CLI, lists three conditions for removing the gate: a published specification, KYAML output in most Kubernetes examples, and no new bug reports.

The two bugs KYAML exists to remove

The KEP's motivation section names the problems. YAML's indentation-based structure is hard to get right, and templating systems make it worse because the user has to manage whitespace outside the YAML context. And unquoted values can be silently coerced into other types: some words are read as booleans, and some underscore-separated or colon-separated values are read as numbers. The SIG CLI post gives the classic example, the Norway bug, where an unquoted NO parses as the boolean false instead of the string NO.

ConcernBlock-style YAMLJSONKYAML
StructureDefined by indentationBraces and bracketsBraces and brackets, indentation is only cosmetic
String valuesUsually unquoted, so type coercion is possibleAlways double-quotedAlways double-quoted
CommentsAllowedNot supportedAllowed
Trailing commasNot applicableRejectedAllowed, and always emitted
KeysUnquotedMust be quotedUnquoted unless not clearly safe
Document headerOptionalNoneA leading three-dash line, so it is not mistaken for JSON

The table is the whole design argument. KYAML keeps what teams like about YAML, comments and trailing commas, and takes JSON's explicit structure and explicit types. The SIG CLI post puts it between JSON and YAML, and notes that YAML calls this flow style as opposed to block style. The practical payoff is in templating: because structure no longer depends on whitespace, patching a manifest from Helm or another template engine cannot silently change its shape.

What the output looks like and the exact rules

The same Pod from the SIG CLI post, first as ordinary YAML and then as KYAML. The KEP adds the formatting details the post leaves out: two-space indentation, empty collections rendered as empty braces or brackets, paired brackets placed on the same line as the preceding item except where comments need their own line, and multi-line strings written with escaped newlines and line folding instead of block literals.

# Standard YAML (the SIG CLI post's example Pod)
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: demo
spec:
  containers:
    - name: nginx
      image: nginx:1.20

# The same Pod as KYAML: leading --- header, braces and brackets,
# every value double-quoted, trailing commas always emitted.
---
{
  apiVersion: "v1",
  kind: "Pod",
  metadata: {
    name: "my-pod",
    labels: {
      app: "demo",
    },
  },
  spec: {
    containers: [{
      name: "nginx",
      image: "nginx:1.20",
    }],
  },
}

# Why it exists: in block-style YAML this line parses as boolean false,
# not the string "NO" (the "Norway bug"). KYAML would emit country: "NO".
country: NO

Read the KYAML version for what it removed. There is no place where an indentation change alters meaning, and image tag nginx:1.20 is a quoted string whatever it looks like to a number parser. The trailing comma after the last item is not a typo. The KEP says trailing commas are allowed and always emitted, which should keep a one-line change a one-line diff.

Because output is deterministic, the KEP lists consistent conversion as a goal: repeated conversion should produce the same output. That property is what makes KYAML usable as a canonical form in a repository, which the CI section below relies on.

Which kubectl versions support it, and the default-output trap

The version split matters for fleets. Producing KYAML with kubectl needs 1.34 or later, and on 1.34 only with the environment variable. Consuming KYAML needs nothing, because it is YAML. So a repository can adopt KYAML files today without waiting for every engineer's kubectl to upgrade.

Kubernetes releaseKYAML stageHow you get it
v1.34AlphaExport KUBECTL_KYAML=true, then run kubectl get with -o kyaml
v1.35BetaEnabled by default, but you still pass -o kyaml; setting the variable to false turns it off
v1.37StableThe gate is gone and the format is always available; conformance testing is complete
Any versionInputKYAML files are valid YAML, so any kubectl can read them as manifests

The SIG CLI post states there are no current plans to make KYAML the default output format. If you want it as your own default, the route is kuberc, which the docs mark Beta since Kubernetes v1.34 and which lives at the home directory under .kube/kuberc. The post gives the command for 1.36 and later, and a different spelling for 1.33 to 1.35 where the alpha prefix is still required.

# Kubernetes 1.35+ (Beta, enabled by default): the flag is still required.
kubectl get deployment my-app -o kyaml

# Kubernetes 1.34 (Alpha, opt-in): set the gate first.
export KUBECTL_KYAML=true
kubectl get deployment my-app -o kyaml

# Save it; this writes the live object as KYAML.
kubectl get deployment my-app -o kyaml > my-app.kyaml

# Make it your own default for get (no plan to change the cluster-wide default).
# Kubernetes 1.36+:
kubectl kuberc set --section defaults --command get --option output=kyaml
# Kubernetes 1.33 to 1.35 (alpha prefix still required):
kubectl alpha kuberc set --section defaults --command get --option output=kyaml

# My own composition, not copied from the docs: the defaults shape from the
# kuberc reference (which documents interactive for delete) with output for get.
# ~/.kube/kuberc
apiVersion: kubectl.config.k8s.io/v1beta1
kind: Preference
defaults:
- command: get
  options:
    - name: output
      default: kyaml

The file form below is my own composition. It copies the defaults shape from the kuberc reference, where the documented example sets interactive for delete, and swaps in the get command and the output option that the blog's command sets. Run the documented command once and read what it writes before trusting my version.

Using kubectl get -o kyaml in CI diffs

The use case worth adopting this for is a pull request diff that shows only what changed in meaning. Two checks follow from the documented behaviour. First, a formatting gate on manifests committed to the repository: the Kubernetes yamlfmt prints the KYAML version of a file to stdout, so a gate can compare the committed file with its own canonical form. Second, a snapshot of a live object, written through kubectl.

#!/usr/bin/env bash
# Composed from the documented commands. NOT run against a cluster by me:
# test the exit-code handling before you let it gate a merge.
set -euo pipefail

# 1. Formatting gate. sigs.k8s.io/yaml/yamlfmt prints KYAML to STDOUT and
#    never edits the file, so compare its output with the committed file.
go install sigs.k8s.io/yaml/yamlfmt@latest   # pin a real version in CI
status=0
for f in k8s/*.yaml; do
  tmp="$(mktemp)"
  yamlfmt -o=kyaml "$f" > "$tmp"
  if ! diff -u "$f" "$tmp"; then
    echo "not canonical KYAML: $f" >&2
    status=1
  fi
  rm -f "$tmp"
done

# 2. Drift snapshot of a live object. Compare it with the previous snapshot
#    of the SAME object; status and other server fields make a diff against
#    the source manifest noisy.
kubectl get deployment my-app -o kyaml > "snapshots/my-app.$(date +%F).kyaml"

exit "$status"

# Wrong: yamlfmt -o=kyaml k8s/app.yaml > k8s/app.yaml   (truncates the file first)
# Right: write to a temp file, inspect it, then mv it into place.

I have composed the script from the documented commands and have not run it against a cluster, so treat the exit-code handling as the part to test first. One expectation I would verify rather than assume: a live object carries server-populated fields such as status, so a snapshot diff against a source manifest will be noisy. Snapshot the live object against an earlier snapshot of the same object, not against the file in git.

Pin the formatter. KEP 5295 lists lock-in to byte-level output as a risk and says the specification will leave room where it is not confident. Installing with the latest tag in CI means a formatter upgrade can rewrite every manifest at once. Pin a version and upgrade deliberately.

Three ways to produce KYAML: kubectl, two yamlfmt tools

The SIG CLI post lists three routes. kubectl converts live objects. The Kubernetes yamlfmt, at sigs.k8s.io/yaml, converts files; it prints to stdout, accepts a directory, and has a diff flag. Google's yamlfmt added a dedicated kyaml formatter in v0.21.0, which also ships as a pre-commit hook and a Docker image for CI.

The configuration for Google's tool is two lines. The post says the kyaml formatter takes no additional configuration and does not share options with the default formatter, so mixing the two causes an error. If your repository already has a yamlfmt file with indentation or line-length options, those options must be removed when you switch the formatter type.

# .yamlfmt  (Google's yamlfmt, kyaml formatter added in v0.21.0)
formatter:
  type: kyaml

# The kyaml formatter takes no extra options and shares none with the default
# formatter; leaving indent or line_length keys in this file is an error.
#   yamlfmt -dry k8s/app.yaml     preview without modifying the file
#   yamlfmt k8s/app.yaml          rewrite in place
#   yamlfmt ./k8s/                convert a whole directory

Which one to pick is a workflow question rather than a quality question. Use the Kubernetes tool when you want a one-off conversion or a stdout check in a script. Use Google's when you want a formatter that rewrites files in place, runs in pre-commit and reads a config file.

Trade-offs and the ways this goes wrong

The KEP is unusually candid about risks, and they are the right checklist. Net-worse readability: flow style with nested braces can be harder to scan than block style, which is why the plan was to test with many examples and collect feedback. Invalid output: the encoder could emit invalid YAML, which would be a bug. And a false sense of safety: KYAML avoids YAML's pitfalls but does not fix YAML itself.

The third risk is the one I would brief a team on. Quoting every string stops the Norway bug in files that were written or formatted as KYAML. It does nothing for a hand-written block-style manifest sitting next to it with an unquoted value, and it does not validate that the manifest is a correct Kubernetes object. Schema validation still has to happen somewhere else.

The Kubernetes yamlfmt writes the converted file to stdout. It does not edit the file. Running it on a file and expecting an in-place rewrite changes nothing, and redirecting its output onto the same path can truncate the source before it has been read. Write to a temporary file, check it, then move it into place.

There is also a practical cost the documentation cannot remove. A one-time conversion rewrites every line of every manifest, so the first pull request is unreadable by design. Do the conversion in its own commit with no other changes, and tell reviewers that this one is checked by running the formatter, not by reading it.

What to do on Monday

Treat KYAML as a formatting decision for one directory, not an organisation-wide migration. The KEP lists requiring projects to switch by default as a non-goal, and nothing in v1.37 forces your hand.

  1. Check what produces KYAML in your fleet: run kubectl version and note anything older than 1.34.
  2. Try kubectl get on one live deployment with -o kyaml, and read the output next to the equivalent -o yaml.
  3. Pick one manifest directory and convert it in a commit containing nothing else, using a pinned yamlfmt version.
  4. Add the formatter as a pre-commit hook or a CI check that fails on a non-empty diff.
  5. Decide whether you want kuberc to default your own get output to kyaml, and leave the rest of the team's defaults alone.
# 1. What can produce KYAML in your fleet? Anything below 1.34 cannot.
kubectl version

# 2. Read the two formats side by side on one live object.
kubectl get deployment my-app -o yaml  > /tmp/my-app.yaml
kubectl get deployment my-app -o kyaml > /tmp/my-app.kyaml

# 3. See what converting one committed manifest would change (diff, no write).
yamlfmt -o=kyaml -d k8s/app.yaml

After a month, ask a concrete question: did any review catch a whitespace or type-coercion mistake that the old format would have hidden? If not, you have a tidier repository and nothing more, which is still a fair result for a formatting change.

KYAML reaching Stable does not change a single manifest you already have. What it gives you is a canonical, whitespace-proof, always-quoted form that kubectl can emit and a formatter can enforce, with every file still readable by any kubectl. Adopt it where a diff matters, pin the tool that produces it, and keep schema validation separate, because a safer syntax is not a correct manifest.

FAQ

Frequently Asked Questions

Short answers to what readers ask most about this topic.

What is KYAML in Kubernetes?

KYAML is a safer, less ambiguous subset of YAML designed for Kubernetes, and every KYAML file is valid YAML. It always double-quotes strings, uses braces for maps and brackets for lists, allows comments and trailing commas, and starts with a three-dash header. It does not replace YAML.

Which Kubernetes version makes KYAML Stable?

KYAML graduated to Stable in Kubernetes v1.37, with conformance testing complete and kubectl get -o kyaml now Stable. It was Alpha in v1.34 and Beta in v1.35. The work is tracked in KEP 5295 under SIG CLI.

How do I use kubectl get -o kyaml?

Run kubectl get deployment my-app -o kyaml on Kubernetes 1.35 or later. On 1.34 you first export KUBECTL_KYAML=true. Redirect the output to a file if you want to keep it.

Do I have to rewrite my existing manifests as KYAML?

No. The v1.37 release notes say existing manifests, tooling and pipelines do not need to change. Because KYAML is valid YAML, any kubectl version can read a KYAML file, and block-style YAML keeps working.

Does KYAML fix the Norway bug in YAML?

In files written as KYAML, yes, because every value string is double-quoted and so cannot be coerced to a boolean or number. It does not protect a hand-written block-style manifest with an unquoted value. KEP 5295 itself lists a false sense of safety as a risk, since KYAML does not fix YAML itself.