TRENDING
Rows of identical brass-colored apartment mailboxes with small locks and name labels along an orange corridor wall
October 9, 2026
How to Prevent Broken Object Level Authorization (IDOR) in a FastAPI App
Street-level upward view of the Monetary Authority of Singapore building and neighbouring office towers under a pale sky
October 9, 2026
Singapore’s AI Guidelines Turn Independent Review Into a Question of Who Sets the Risk Rating
Cast-iron late Qing dynasty coin minting press with a large flywheel, displayed in a museum case
October 9, 2026
Attackers Hijacked the .gh, .sl and .as Country Domains and Minted HTTPS Certificates for Google
Rows of closed oak library card catalog drawers, each with a brass pull and a blank label holder
October 9, 2026
How to Encrypt PII in Python and Keep It Searchable With Blind Indexes
Close-up of a vintage Western Electric manual telephone switchboard with orange lamps, red patch cords plugged into jacks, a rotary dial and a black handset
October 9, 2026
Microsoft’s Agent Lightning v1.0 Turns Agent Training Into a Sample-Accounting Problem
09 Oct 2026
SXZ.io SXZ.io
  • Home
Search the Site
Popular Searches:
Technology Amazon AI
Recent Posts
Two orange safety relief valves on grey pressure vessels in an industrial plant
How to Add Backpressure and Load Shedding to a Python Service Before Overload Takes It Down
October 8, 2026
Yellow diamond-shaped merging traffic warning sign showing a side road joining a main road
GitHub’s Git Rebuild Turns Repository Durability and Read Scale Into Two Separate Problems
October 8, 2026
A lugworm lying on wet sand and mud at low tide
A Compromised Admin Account Put the Shai-Hulud Worm Into AI Sandbox Maker Tensorlake’s npm SDK
October 8, 2026
SXZ.io SXZ.io
  • Home

Categories

Articles 232 Posts
News 234 Posts
Learning Hub 204 Posts
Home/Learning Hub/How to Use kubectl’s New KYAML Output Format to Avoid YAML’s Norway Bug
Learning Hub

How to Use kubectl’s New KYAML Output Format to Avoid YAML’s Norway Bug

Learn what causes YAML's classic Norway bug, reproduce it yourself with real commands, and use kubectl's new -o kyaml output format to avoid it entirely, no live cluster required.

August 12, 2026 13 Min Read
38

If you have ever written enabled: no in a YAML file to mean “the string no” and had some tool quietly treat it as “false” instead, you have already met the bug this tutorial is about. It has a nickname: the Norway problem, because the two-letter country code for Norway, “NO”, is one of several everyday words that standard YAML will silently convert into a boolean unless you quote it. Kubernetes’ command line tool, kubectl, just shipped a real, working answer to this: a new output format called KYAML, defined in a Kubernetes Enhancement Proposal (KEP) numbered 5295.

Table Of Content

  • What Is KYAML, and Why Does Kubernetes Need It?
  • The Norway Problem, in One Sentence
  • How Official and How New Is This?
  • Prerequisites
  • Step 1: Reproduce YAML’s Type Coercion Bug Yourself
  • Step 2: Install a kubectl Version That Supports KYAML
  • Linux
  • macOS
  • Windows
  • Step 3: Confirm Your kubectl Recognizes the kyaml Format
  • Step 4: Generate Your First KYAML Output
  • Step 5: Reproduce the Norway Bug Inside kubectl Itself
  • Step 6: Fix It, and Prove KYAML Round-Trips Cleanly
  • Common Mistakes and Gotchas
  • How to Verify Everything Worked
  • Next Steps

This tutorial teaches you what KYAML actually is, why YAML has this problem in the first place, and how to use kubectl’s new -o kyaml flag today. Every command below was run for real while writing this, using a freshly downloaded kubectl v1.36.3 binary and Python 3.13 with PyYAML 6.0.3, and every terminal block you see is copied verbatim from that session. You will not need a running Kubernetes cluster for any of it: every example uses kubectl config view, one of the few kubectl commands that works entirely against a local file with no API server involved, so you can follow along on a laptop with nothing installed yet.

What Is KYAML, and Why Does Kubernetes Need It?

YAML supports two ways of writing the same data. The style almost everyone uses, with indentation and no brackets, is called block style. A Kubernetes Pod written in block style looks like this:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: demo
spec:
  containers:
    - name: nginx
      image: nginx:1.20

YAML also supports flow style, which uses {} for maps and [] for lists, closer to what JSON looks like. KYAML is a specific, disciplined way of writing flow-style YAML. The Kubernetes Blog post introducing it describes KYAML as “a strict subset (or dialect) of standard YAML, designed to be parseable by the existing ecosystem without any changes,” and its KEP summary uses almost the same language, calling it a strict subset “aka dialect” of standard YAML. The same Pod, written as KYAML, looks like this:

---
{
  apiVersion: "v1",
  kind: "Pod",
  metadata: {
    name: "my-pod",
    labels: {
      app: "demo",
    },
  },
  spec: {
    containers: [{
      name: "nginx",
      image: "nginx:1.20",
    }],
  },
}

Notice three rules in that second block: every map uses {}, every list uses [], and every string value is wrapped in double quotes. Nothing here is a new file format. Every byte of that KYAML block is still completely ordinary YAML; any tool that already reads YAML, including kubectl itself, can read it with zero changes. What KYAML adds is a set of authoring rules that remove ambiguity, and the specific ambiguity it is most concerned with is the one that gives this tutorial its running example.

The Norway Problem, in One Sentence

In YAML, quoting a string value is optional, and unquoted words like yes, no, on, off, true, and false are read as booleans instead of text, regardless of what you meant. If a country’s ISO code, “NO” for Norway, ends up as an unquoted YAML value, most YAML parsers will read it as the boolean false, silently throwing away the fact that you meant a country. KYAML’s answer is blunt and effective: quote every string value, always, so there is nothing left for a parser to guess about. You are about to watch this exact failure happen on your own machine, then see how KYAML output sidesteps it.

How Official and How New Is This?

KEP-5295 is owned by Kubernetes’ SIG CLI and authored by Tim Hockin and Benjamin Elder, both long-standing Kubernetes maintainers. Its own tracking metadata lists a clear progression:

  • Alpha in Kubernetes v1.34, gated behind an environment variable, KUBECTL_KYAML, that had to be explicitly set to enable it.
  • Beta in Kubernetes v1.35, the stage where Kubernetes convention turns a feature on by default while still allowing it to change.
  • Stable, targeted for Kubernetes v1.37, which has not shipped yet. As of this writing the latest stable release is v1.36.3, so KYAML is currently a beta feature, not a finished, locked-in one.

That beta status matters in practice: this tutorial confirms below that -o kyaml already works with no environment variable needed on a current kubectl, but exact formatting details could still change before the feature reaches v1.37 and stable status. Treat everything you see here as “how it behaves today,” not as a permanent guarantee.

Prerequisites

You will need:

  • A terminal on Windows, macOS, or Linux. This tutorial was written and tested on Windows using Git Bash, but every command shown is the same on macOS and Linux aside from the install step.
  • curl, to download the kubectl binary directly from Kubernetes’ own release servers.
  • Python 3.8 or newer with pip, used only for a short, self-contained demonstration of the underlying YAML ambiguity. You do not need any Kubernetes-specific Python library.
  • No running Kubernetes cluster, no Docker, and no cloud account. Every kubectl command in this tutorial is kubectl config view against a local file, which never contacts a server.

Step 1: Reproduce YAML’s Type Coercion Bug Yourself

Before touching kubectl at all, it helps to see the underlying problem with nothing but a plain YAML library, so you know exactly what KYAML is defending against. Install PyYAML:

python -m pip install pyyaml

Now create a file called norway_bug_demo.py with this content:

import yaml

doc = """country: NO
enabled: no
flag: on
port: "8080"
"""

result = yaml.safe_load(doc)
for key, value in result.items():
    print(f"{key!r} -> {value!r} ({type(value).__name__})")

Run it:

python norway_bug_demo.py

Here is the real output from running that exact script:

'country' -> False (bool)
'enabled' -> False (bool)
'flag' -> True (bool)
'port' -> '8080' (str)

What just happened: you wrote country: NO meaning the two-letter code for Norway, and PyYAML’s safe_load handed you back the Python boolean False, silently. The same thing happened to enabled: no and flag: on. Only port, because you wrapped “8080” in quotes, came back as the string you actually wrote. This is not a PyYAML bug; PyYAML is correctly implementing YAML’s rule that unquoted yes/no/true/false/on/off (in any capitalization) are boolean literals unless you quote them. It is worth knowing that this specific word list is not universal across every YAML tool in every language; different YAML libraries can and do disagree slightly on which words count as booleans, which is part of why the safest fix is not “memorize the dangerous words” but “quote every string, always.” That is precisely KYAML’s rule.

One detail worth testing yourself: single letters behave differently from full words. If you add y: y and n: n to the same document and reload it, PyYAML 6.0.3 leaves those as the strings "y" and "n", not booleans, even though the full words yes and no do get converted. Do not assume abbreviations are automatically safe just because you tested one YAML library; the point of this tutorial is to stop relying on any parser’s specific quirks at all.

Step 2: Install a kubectl Version That Supports KYAML

KYAML output needs kubectl v1.34 or newer, and you want v1.35 or newer to get it without any extra flags. Kubernetes publishes kubectl as a single static binary with no installer and no dependencies, so installing a specific version is just a download.

Linux

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x ./kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

macOS

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl

Use darwin/amd64 instead of darwin/arm64 if you are on an Intel Mac. If you use Homebrew, brew install kubectl works too.

Windows

curl -LO "https://dl.k8s.io/release/v1.36.3/bin/windows/amd64/kubectl.exe"

Whichever platform you used, confirm the download worked and check the version:

kubectl version --client

Real output from this tutorial’s test machine:

Client Version: v1.36.3
Kustomize Version: v5.8.1

Any v1.36.x client will behave identically for everything in this tutorial. If kubectl version --client shows something older than v1.34, KYAML will not be available yet; download a newer binary the same way.

Step 3: Confirm Your kubectl Recognizes the kyaml Format

kubectl happens to tell you its full list of supported output formats if you deliberately give it one that does not exist. Try it:

kubectl config view -o bogus

Real output:

error: unable to match a printer suitable for the output format "bogus", allowed formats are: go-template,go-template-file,json,jsonpath,jsonpath-as-json,jsonpath-file,kyaml,name,template,templatefile,yaml

kyaml is sitting right there in that list, alongside the familiar yaml and json. This is a useful trick to keep: whenever you are not sure which output formats a given kubectl build supports, pass an invalid one and read the error. Note that this trick only works instantly with commands like config view that never need to contact a cluster; commands like kubectl get try to connect to a live API server first and will fail on the connection before they even look at your -o flag, so you cannot use this specific trick with get unless you already have a reachable cluster.

You do not need to set KUBECTL_KYAML for this to work. That environment variable was required back in Kubernetes v1.34’s alpha stage; now that the feature is in beta, it is enabled by default, exactly as Kubernetes convention says a beta feature should be. This tutorial’s test machine confirmed this directly: setting KUBECTL_KYAML=true before a command and leaving it unset produced identical output every time.

Step 4: Generate Your First KYAML Output

Rather than touch your real kubeconfig, point kubectl at a throwaway file for the rest of this tutorial by setting the KUBECONFIG environment variable. This is a normal, safe kubectl feature: whatever path you give it there is used instead of the default ~/.kube/config, and nothing you do to it can affect your real cluster access.

Create a file called kubeconfig-demo.yaml with the following content. This is an ordinary, valid kubeconfig describing one fake cluster, one fake user, and one context, with a fake bearer token so it is safe to print on screen:

apiVersion: v1
kind: Config
clusters:
- name: demo-cluster
  cluster:
    server: https://demo.example.com:6443
contexts:
- name: demo-context
  context:
    cluster: demo-cluster
    user: demo-user
    namespace: "no"
users:
- name: demo-user
  user:
    token: fake-token-for-demo
current-context: demo-context

Notice the namespace is deliberately named "no", quoted, standing in for something like a project or team codename. Keep that detail in mind; it becomes the whole point in the next step. Now render it both ways:

KUBECONFIG=./kubeconfig-demo.yaml kubectl config view --raw -o yaml
apiVersion: v1
clusters:
- cluster:
    server: https://demo.example.com:6443
  name: demo-cluster
contexts:
- context:
    cluster: demo-cluster
    namespace: "no"
    user: demo-user
  name: demo-context
current-context: demo-context
kind: Config
users:
- name: demo-user
  user:
    token: fake-token-for-demo
KUBECONFIG=./kubeconfig-demo.yaml kubectl config view --raw -o kyaml
---
{
  kind: "Config",
  apiVersion: "v1",
  clusters: [{
    name: "demo-cluster",
    cluster: {
      server: "https://demo.example.com:6443",
    },
  }],
  users: [{
    name: "demo-user",
    user: {
      token: "fake-token-for-demo",
    },
  }],
  contexts: [{
    name: "demo-context",
    context: {
      cluster: "demo-cluster",
      user: "demo-user",
      namespace: "no",
    },
  }],
  current-context: "demo-context",
}

Why the --raw flag: by default, kubectl config view hides sensitive values and prints REDACTED instead of the real token, which is the right behavior for your actual kubeconfig. --raw tells it to print real values instead. Only use --raw on throwaway files like this one; never run it against your real kubeconfig where someone else might see your screen or a recording.

Compare the two outputs closely and you will find two real differences beyond the obvious brace-and-bracket change. First, every string value in the KYAML version is quoted, including "no", while the YAML version only quotes it because kubectl’s YAML printer is smart enough to know this particular field is typed as a string and protects it; an unquoted value you type by hand does not get that protection, which is exactly what Step 5 demonstrates. Second, look at field order: the YAML output is alphabetical (apiVersion, clusters, contexts, current-context, kind, users), while the KYAML output preserves the object’s natural field order (kind, apiVersion, clusters, users, contexts, current-context), the same kind-and-apiVersion-first ordering you are used to seeing in every example Kubernetes manifest online. That is a real, observable side effect of how each printer is implemented, not something either format’s specification requires.

Step 5: Reproduce the Norway Bug Inside kubectl Itself

Now recreate the classic mistake. Edit kubeconfig-demo.yaml and remove the quotes around the namespace, so the line reads exactly:

    namespace: no

This looks like an entirely harmless edit. You are not changing what you mean, a namespace named “no”, just tidying up what looks like unnecessary punctuation. Try to view the config now:

KUBECONFIG=./kubeconfig-demo.yaml kubectl config view --raw -o yaml

Real output:

error: error loading config file "./kubeconfig-demo.yaml": json: cannot unmarshal bool into Go struct field Context.contexts.context.namespace of type string

What actually happened: kubectl’s YAML loader parsed no as the boolean false, the same YAML ambiguity you saw in Step 1’s Python demo. It then tried to place that boolean into the namespace field, which is defined as a string in kubectl’s own source code, and Go’s strict JSON unmarshaling refused, because a bool is not a string. This is a real, current kubectl v1.36.3 error message, and it is a good example of why this bug is so persistently confusing to beginners: nothing about “cannot unmarshal bool into Go struct field… of type string” mentions YAML, quoting, or Norway. Someone hitting this for the first time has no obvious clue that the fix is to add quotation marks; they are far more likely to assume something is wrong with their cluster, their token, or kubectl itself.

It is worth being precise about what kind of bug this is. Because namespace is a strongly typed string field, the mistake surfaces as a loud, if cryptic, error rather than silently corrupting your data. Many Kubernetes fields are typed just as strictly, so you will often get an error like this one instead of a silent failure. The genuinely silent version of this bug tends to show up in places with looser typing, such as Helm chart values files, or custom resources whose schema allows arbitrary nested data. There, the same unquoted no can become a real boolean with no error raised at all, and the mistake only surfaces later as unexpected application behavior.

Step 6: Fix It, and Prove KYAML Round-Trips Cleanly

Put the quotes back:

    namespace: "no"

Confirm it loads again, then save the KYAML rendering to a new file:

KUBECONFIG=./kubeconfig-demo.yaml kubectl config view --raw -o kyaml > roundtrip.yaml

roundtrip.yaml now contains the flow-style, fully quoted KYAML block from Step 4. Here is the real test of the KEP’s central claim, that KYAML is “designed to be parseable by the existing ecosystem without any changes”: point kubectl at that KYAML file as if it were an ordinary kubeconfig, with no special flag telling it what format to expect.

KUBECONFIG=./roundtrip.yaml kubectl config view --raw -o yaml

Real output:

apiVersion: v1
clusters:
- cluster:
    server: https://demo.example.com:6443
  name: demo-cluster
contexts:
- context:
    cluster: demo-cluster
    namespace: "no"
    user: demo-user
  name: demo-context
current-context: demo-context
kind: Config
users:
- name: demo-user
  user:
    token: fake-token-for-demo

That is a perfect, complete round trip. kubectl’s ordinary YAML loader, the exact same code path used for every kubeconfig and every manifest, opened a KYAML file with zero special handling and reconstructed the data correctly, namespace "no" included. This is the practical payoff of KYAML being “still valid YAML” rather than a new format: you do not need a new parser, a plugin, or a converter to consume it. Anything that reads YAML today already reads KYAML.

Common Mistakes and Gotchas

  • Treating KYAML as stable. As of this writing it is a beta feature targeting stable status in Kubernetes v1.37, which has not shipped. Formatting details could still change. Pin your kubectl version in any script or CI pipeline that parses -o kyaml output, the same discipline you should already apply to anything relying on a beta API.
  • Expecting kubectl to preserve your hand-written comments. KYAML’s grammar allows comments, but kubectl config view -o kyaml (like -o yaml and -o json) generates its output fresh from the decoded, in-memory object. Comments in your original file are gone by the time kubectl gets there; there is nothing left to carry over.
  • Assuming the file extension or format alone prevents the Norway bug. KYAML’s safety comes from the discipline of always quoting strings, not from the flow-style brackets themselves. When kubectl generates KYAML, it never forgets to quote, because it always knows the real type of every field. If you hand-write a .kyaml file yourself and forget a quote, the underlying YAML parser will still read no as a boolean; nothing about the .kyaml name changes that. The protection is strongest exactly where you saw it in this tutorial: letting a tool that already knows your data’s real types generate the file for you.
  • Trying the -o bogus discovery trick on commands that need a cluster. kubectl get, create, and apply all try to reach a live API server before they validate your output format flag, so that trick only works instantly on cluster-independent commands like config view.
  • Assuming every YAML tool agrees on which words are dangerous. This tutorial’s own test showed PyYAML treating full words like yes/no/on/off as booleans while leaving single letters y/n as strings. Other YAML libraries, including the ones Kubernetes itself uses internally, are not guaranteed to draw the line in exactly the same place. Quote every string value and you never have to know or care where that line is.

How to Verify Everything Worked

Before moving on, you should be able to check off all of the following, using only the commands from this tutorial:

  • python norway_bug_demo.py prints False for country and enabled, and True for flag, proving you understand the underlying YAML ambiguity independently of Kubernetes.
  • kubectl version --client reports v1.34 or newer, ideally v1.35 or newer so KYAML works with no extra flags.
  • kubectl config view -o bogus lists kyaml among the allowed formats.
  • kubectl config view --raw -o kyaml against your demo kubeconfig produces flow-style output with every string quoted and kind/apiVersion first.
  • Removing the quotes from namespace: "no" and reloading produces the cannot unmarshal bool error, and restoring them fixes it.
  • Saving KYAML output to a file and pointing KUBECONFIG at that file loads correctly with no errors, proving the round trip.

Next Steps

Everything here used a local kubeconfig file specifically because it needs no cluster. -o kyaml is registered through the same shared output-formatting code that already handles -o yaml and -o json for commands like get, so it should be available there too, though this tutorial only ran it against config view and did not test it against a live cluster. If you have access to a cluster, or want to spin up a local one with kind or minikube, try kubectl get pod <name> -o kyaml or kubectl get deployment <name> -o kyaml and compare it to the familiar -o yaml version of the same object.

Since KYAML is still evolving, watch KEP-5295 for its progress toward stable status in v1.37, and if you want to weigh in, SIG CLI discusses this work in the #sig-cli channel on Kubernetes Slack. You may also see kyaml.dev referenced elsewhere as a shortcut for KYAML’s documentation; as of this writing that address simply redirects to the KEP’s page on GitHub rather than hosting an interactive tool, so the install-and-test path in this tutorial is the most reliable way to try it yourself today.

Finally, take five minutes to grep your own YAML files, Kubernetes manifests, Helm values, CI configs, anywhere you write YAML by hand, for unquoted yes, no, on, off, or country-code-style values. Every one you find and fix is one fewer confusing “cannot unmarshal bool” error waiting for whoever edits that file next.

Tags:

Command LineDevOpskubectlKubernetesYAML

Share

Aerial view of the Pentagon building in Washington, D.C.
Previous Post

Palantir’s $244 Million No-Bid Pentagon Deal Turns AI Procurement Into a Conflict-of-Interest Test

Three hands photographed in dramatic black-and-white lighting, spelling out letters in American Sign Language against a black background
Next Post

Google DeepMind’s SL2T Model Brings Sign-to-Text Dictation to the Pixel 11

No Comment! Be the first one.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Latest
08 Oct
How to Add Backpressure and Load Shedding to a Python Service Before Overload Takes It Down
08 Oct
GitHub’s Git Rebuild Turns Repository Durability and Read Scale Into Two Separate Problems
Trending
October 8, 2026
How to Add Backpressure and Load Shedding to a Python Service Before Overload Takes It Down
October 8, 2026
GitHub’s Git Rebuild Turns Repository Durability and Read Scale Into Two Separate Problems
October 8, 2026
A Compromised Admin Account Put the Shai-Hulud Worm Into AI Sandbox Maker Tensorlake’s npm SDK
October 8, 2026
How to Prevent Broken Object Level Authorization (IDOR) in a FastAPI App
October 8, 2026
Singapore’s AI Guidelines Turn Independent Review Into a Question of Who Sets the Risk Rating
October 8, 2026
Attackers Hijacked the .gh, .sl and .as Country Domains and Minted HTTPS Certificates for Google

Related Posts

A laptop wrapped in a chain and padlock, illustrating least-privilege controls for AI agents.
Learning Hub

How to Secure Tool-Using AI Agents Before They Touch Production

June 8, 2026
Colorful sticky notes arranged on an office wall, symbolizing governance checklists and planning.
Learning Hub

AI Governance for Agentic Apps: A Practical Checklist for Builders

June 8, 2026
A technician connects green fiber optic cables at a data center, representing a private production inference endpoint.
Learning Hub

How to Deploy a Fine-Tuned LLM Behind a Private Production Inference Endpoint

June 8, 2026
Narrow aisle behind black supercomputer racks in a data center
Learning Hub

Kubernetes SELinux Volume Labeling: What Cluster Operators Should Audit Before v1.37

June 8, 2026
SXZ.io SXZ.io
  • [email protected]

Categories

Articles
Learning Hub
News

All Rights Reserved by SXZ.io ©2026