OnlyFormat

YAML vs JSON for Configuration Files: Which Should You Use?

OnlyFormat Editorial Team···8 min read

"YAML vs JSON" sounds like a single decision, but it's really two. For machine-to-machine data exchange — REST APIs, web app state, RPC payloads — JSON won a decade ago and there's no real debate. The live question is about configuration files: the ones humans write, review in pull requests, and edit at 2am during an incident. That's where YAML earns its keep and where its landmines also live. This guide walks through the real differences with Kubernetes and GitHub Actions examples, then gives you a decision tree.

1. This is a config-file question, not an API-payload question

JSON became the default wire format for the web because it's fast to parse, maps cleanly to JavaScript objects, and has an unambiguous specification (RFC 8259). Almost every REST API on the internet speaks JSON. Nobody is seriously proposing to replace that with YAML — YAML's complexity is unnecessary overhead on the wire, and JSON's speed advantage matters when you're parsing millions of payloads a second.

Configuration files are a different workload. They're loaded once at startup, edited by humans, reviewed in code review, and live in Git for years. Parsing speed is irrelevant; readability and editability are everything. That's the scope of this comparison: which format is better to edit a 300-line Kubernetes manifest or a CI pipeline definition, not which is better to serialise a shopping cart over HTTP.

2. Readability, with real examples

The clearest way to see the difference is to put the same Kubernetes Deployment in both formats. Here's the YAML version a platform engineer might write and review:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: ghcr.io/example/web:1.4.0
          ports:
            - containerPort: 8080

And the semantically identical JSON:

{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "web",
    "labels": { "app": "web" }
  },
  "spec": {
    "replicas": 3,
    "selector": { "matchLabels": { "app": "web" } },
    "template": {
      "metadata": { "labels": { "app": "web" } },
      "spec": {
        "containers": [
          {
            "name": "web",
            "image": "ghcr.io/example/web:1.4.0",
            "ports": [{ "containerPort": 8080 }]
          }
        ]
      }
    }
  }
}

Both files encode the same object graph, but the YAML version is dramatically easier to scan. There are no quoted keys, no trailing commas to trip over, no doubled-up closing braces. When you review this in a pull request, your eye tracks the indentation down to containers and you read the configuration as prose. The JSON version forces you to parse punctuation before you can parse meaning. The same pattern holds for GitHub Actions workflows, Docker Compose files, and Ansible playbooks — anywhere you have a nested hierarchy, YAML's reduced punctuation noise compounds into real ergonomic wins.

3. YAML's power features

YAML has four capabilities that JSON simply does not, and each one earns its keep in real configs.

Comments. YAML supports # line comments. In a production config where half the fields exist for historical reasons, being able to write timeout: 30 # raised from 10 after 2025-11 incident next to the value is worth more than any amount of parsing speed. JSON has no native comments, which is the single most common complaint from teams that pick JSON for config.

Anchors and aliases. YAML lets you define a block once with &name and reuse it with *name. This is genuinely useful when the same environment block appears in four services of a docker-compose file, or when three Kubernetes containers share the same resource limits.

Multi-line strings. YAML has two block scalar styles — | preserves newlines, > folds them into spaces — which makes embedding shell scripts, SQL queries, or long descriptions readable without \n escape soup. Anyone who's tried to paste a 40-line startup script into a JSON value knows the pain.

Multiple documents in one file. A single YAML file can contain multiple documents separated by ---, which is why kubectl apply -f all.yaml can create ten resources from one file. JSON has no equivalent — you'd need an array wrapper and a different tool convention.

4. YAML's landmines

YAML's power comes from inferring structure and types. That inference has sharp edges.

Indentation sensitivity. YAML uses whitespace to define nesting. Mix tabs and spaces and the parser either rejects the file (at best) or silently interprets a key as a child of the wrong parent (at worst). Auto-formatting chat clients and word processors that replace regular spaces with non-breaking spaces will break YAML in ways that are invisible to the human reviewer.

The Norway problem. In YAML 1.1, the two-letter country code for Norway — NO — is parsed as the boolean false unless you quote it. The same applies to yes, on, off, and their capitalised variants. This bit every software team that ever loaded a country code list from YAML, and it's the origin of the strictyaml project ("The Norway Problem", hitchdev.com/strictyaml). The fix is to quote every string that could be misread as something else, which undercuts the "less punctuation" advantage.

YAML 1.1 vs 1.2 type coercion. YAML 1.2 (2009) dropped the yes/no/on/off booleans and tightened number parsing, but many popular parsers — including widely-used libraries in the Ruby, Python, and Go ecosystems — defaulted to YAML 1.1 behaviour for years. A version string like 1.10 may parse as the float 1.1. A postal code starting with 0 may be interpreted as octal. Always quote values where the type matters.

Trailing whitespace and invisible characters. A stray space after a key, or a zero-width character copy-pasted from a chat thread, can change meaning or break parsing. YAML linters (yamllint, prettier) catch most of these, but only if you actually run them in CI.

Need to convert between YAML and JSON?

OnlyFormat's browser-based converter handles both directions, validates on the fly, and runs entirely in your browser — your configs never leave your device.

5. JSON as config: advantages and pains

JSON's grammar is small enough that you can hold the entire spec in your head: objects, arrays, strings, numbers, booleans, and null. Parsing is unambiguous — there's exactly one way to read any valid document, no indentation to get wrong, no type inference that might surprise you. JSON parsers are available in every language, run fast, and produce identical results across implementations. For programmatically-generated configs (anything emitted by another program), JSON is the right default: no risk of a rogue indent changing meaning, no Norway problem, no version ambiguity.

The pain is the human editing experience. No comments, historically, meant that every JSON config file grew a parallel README.md just to explain the fields. The requirement for quoted keys and string values doubles the punctuation load. Trailing commas are forbidden, so adding a field at the end of a block requires fixing the preceding line's comma. Deep nesting piles up closing braces that align at the same column, making it easy to accidentally close the wrong scope.

Two mitigations exist. JSONC (JSON with comments), used by VS Code for its settings files, allows // and /* */ comments and trailing commas while staying close to standard JSON. JSON5 goes further: unquoted keys, single-quoted strings, multi-line strings via backslash continuation, and more. Neither is universally supported, but if you're picking JSON for a config you control end-to-end, JSONC in particular is a pragmatic upgrade.

6. Where each format dominates in 2026

The ecosystem has converged on some defaults that are worth respecting. Fighting these is more trouble than it's worth.

DomainDominant formatWhy
Kubernetes / HelmYAMLDeep hierarchies, human-authored manifests
GitHub Actions / GitLab CIYAMLMulti-line scripts, comments, readability
Ansible / Docker ComposeYAMLReuse via anchors, inventory-style lists
npm / package.jsonJSONTool-generated, never hand-indented deeply
VS Code settingsJSONCJSON strictness plus editor-friendly comments
Cargo.toml / pyproject.tomlTOMLFlat-ish configs, unambiguous types

TOML is the third option worth knowing in 2026. It was designed specifically for configuration, has less whitespace sensitivity than YAML, and has unambiguous types. It's a poor fit for the Kubernetes-style deeply-nested world (its section syntax gets awkward), but for shallow app-level config (build tools, package metadata), it's now the default in two major ecosystems (Rust's Cargo and Python's packaging).

7. Decision tree: which should you use?

  • Joining an existing ecosystem (Kubernetes, GitHub Actions, Ansible) → use what the ecosystem uses. Don't fight the default. That's almost always YAML.
  • Config is human-authored, deeply nested, needs comments and reuse → YAML. Run yamllint in CI and quote anything that's supposed to be a string.
  • Config is emitted by a program, or consumed by tooling that expects strict parsing → JSON. No ambiguity, fast parsing, universal support.
  • Config is shallow, app-level, type-sensitive (build tools, app settings) → TOML, if your ecosystem supports it. JSONC is a fine alternative.
  • You keep hitting YAML footguns in a specific file → convert just that file to JSON (or quote aggressively in YAML). A wholesale migration is rarely worth it.

Frequently asked questions

Q. Should I migrate my YAML configs to JSON?

A. Almost never. If the config is already working, the cost of rewriting it (and retraining everyone who edits it) far exceeds any parsing-speed benefit. Migrate only if you've hit a concrete YAML footgun that keeps recurring — for example, repeated Norway-problem bugs in a country-code list, or ambiguous type coercion in a field that must be a string. In those cases, converting just the problematic file to JSON (or quoting every string in YAML) is a better fix than a wholesale migration.

Q. Is YAML slower to parse than JSON?

A. Yes, measurably. JSON parsers are roughly 3–10× faster than YAML parsers for the same input because JSON's grammar is simpler and requires no indentation tracking or type inference. For a 10 KB config file loaded once at process startup, this is irrelevant — we're talking microseconds. For a service that parses thousands of YAML documents per second, it matters, and you should either cache the parsed form or switch to JSON at the hot path.

Q. Can YAML do everything JSON can?

A. YAML 1.2 is a strict superset of JSON — any valid JSON document is also a valid YAML 1.2 document and will parse the same way. The reverse isn't true: YAML features like anchors, aliases, multiple documents in one file, and comments have no JSON equivalent. In practice this means you can hand a JSON file to a YAML parser and it'll work, but you can't hand an arbitrary YAML file to a JSON parser.

Q. What about TOML?

A. TOML is the third option, and it's growing fast. It was designed specifically for configuration — less whitespace-sensitive than YAML, more readable than JSON, with unambiguous types. Cargo (Rust) and pyproject.toml (Python) made it the default in their ecosystems. TOML struggles with deeply nested structures (the bracketed section syntax gets awkward past two levels), so it's a poor fit for Kubernetes-style hierarchical configs. For flat or shallow configs, it's often the best pick of the three.

Q. Why does my YAML break when I paste it?

A. Almost always indentation. YAML uses whitespace to define structure, and pasting from a chat client, a PDF, or a word processor can silently convert tabs to spaces (or vice versa), replace regular spaces with non-breaking spaces, or add trailing whitespace that changes how a line is parsed. Run your file through a YAML linter (yamllint, or the OnlyFormat YAML to JSON converter which will fail loudly on invalid input) before committing. Configuring your editor to show whitespace characters also helps.

References

  • YAML 1.2.2 Specification — yaml.org/spec/1.2.2
  • IETF RFC 8259 — The JavaScript Object Notation (JSON) Data Interchange Format
  • "The Norway Problem" — hitchdev.com/strictyaml/why/implicit-typing-removed
  • Kubernetes API Conventions — github.com/kubernetes/community (contributors/devel/sig-architecture/api-conventions.md)
  • JSON5 — json5.org
  • TOML 1.0.0 Specification — toml.io/en/v1.0.0

About the OnlyFormat Editorial Team

OnlyFormat's editorial team is made up of working web developers and image-workflow engineers who ship file-conversion tooling for a living. Every guide is reviewed against primary sources — W3C/WHATWG specifications, IETF RFCs, MDN Web Docs, ISO/IEC media standards, and the official documentation of libraries we actually use in production (libwebp, libjpeg-turbo, libavif, FFmpeg, pdf-lib). We update articles when standards change so the guidance stays current.

Sources we cite: W3C · WHATWG · MDN Web Docs · IETF RFCs · ISO/IEC · libwebp · libavif · FFmpeg · pdf-lib

Related tools on OnlyFormat