YAML, JSON or TOML: Picking a Config Format

Published 2026-10-31

All three describe the same thing: nested maps, lists and scalars. The differences are in what they cost you when a human edits the file by hand — which is what config files are for.

The short version

YAML JSON TOML
Comments Yes No Yes
Indentation-sensitive Yes No No
Multi-line strings Yes, well Painful Yes (""")
Type ambiguity High Low Low
Machine-generated Poorly Ideal Rarely
Best for Human-authored config, CI Data interchange, APIs App/package config

YAML: pleasant until it isn't

YAML reads well. It also has a type system that quietly interprets strings.

The Norway problem

countries:
  - NO     # parsed as boolean false

NO is Norway's country code and also a YAML boolean. The same applies to yes, on, y (in YAML 1.1), and their false counterparts. A list of country codes silently becomes [false, true, ...].

Fix: quote values that could be misread. - "NO".

Indentation errors that still parse

server:
  port: 8080
   host: localhost   # one extra space — this is now a parse error

That one fails loudly, which is fine. The dangerous case is the one that doesn't fail:

items:
  - name: a
    tags: [x, y]
  - name: b
   tags: [z]        # wrong indent → silently becomes part of the previous mapping

Depending on context you get a parse error or a structure that is subtly wrong.

Duplicate keys are silently accepted

timeout: 30
timeout: 60    # most parsers keep the last one, no warning

A merge that leaves two timeout keys loses information with no error. Some linters catch it; the parser will not.

JSON: correct, unreadable, no comments

JSON's strength is that it is unambiguous: no dates, no booleans that look like strings, no comments, no anchors. Any two parsers agree.

That same strictness hurts as a config format:

  • No comments. The most common request in every "should we use JSON for config" thread. You cannot annotate why a value is what it is, so the reasoning lives in a wiki nobody reads.
  • No trailing commas. One missing comma and the whole file fails.
  • Verbose for deep nesting. Five closing braces in a row.

JSON is the right choice for data on the wire and for machine-generated files. It is a poor choice for a file a human will edit weekly.

TOML: boring on purpose

[server]
port = 8080
host = "localhost"   # strings are quoted, so no Norway problem

[server.limits]
timeout_ms = 30_000  # underscores allowed for readability

TOML has explicit types. A string is quoted, so "NO" and NO cannot be confused. Dates are a first-class type. Comments work. No indentation rules.

It is less expressive than YAML for deep nesting — TOML tables get verbose when you have lists of objects — which is why it won for shallow config (Cargo, pyproject.toml, Hugo) and lost for Kubernetes manifests.

The type-ambiguity test

The deciding question is often: can this value be misread?

version: 1.10      # float 1.1 — the trailing zero is gone
version: "1.10"    # string, preserved

Version numbers are the classic casualty. So are:

Value YAML reads it as Intent
1.10 1.1 (float) "1.10"
NO false "NO" (country)
on true "on" (a switch position)
08:30 510 (sexagesimal, YAML 1.1) "08:30" (time)
2026-01-01 a date object a string in some contexts

Every one of these is a real bug that shipped. The general defence is to quote anything that is not obviously a number or a boolean.

Converting between them

Conversion is common when migrating config: a legacy YAML file, a JSON payload from an API, a TOML config for a new tool.

YAML ↔ JSON converts both directions and reports the line number of an indentation error rather than a generic "parse failed" — which matters when the file is 400 lines long. For XML (still common in enterprise config), XML Formatter handles formatting and conversion; for inspecting a large JSON payload, JSON Viewer collapses and validates.

One caution when converting YAML → JSON: quoted scalars survive, unquoted ones may not. version: 1.10 becomes 1.1 in the JSON output, because the parser already interpreted it as a float. The information was lost before the conversion.

The recommendation

If the file is... Use
Hand-edited app config TOML
Hand-edited CI / k8s manifests YAML (quote aggressively)
Exchanged between systems JSON
Generated by a program JSON
Deeply nested with lists of objects YAML, or JSON if comments do not matter

The one-line rule: use the format whose failure mode you can debug. YAML's failures are silent and subtle; JSON's are loud and trivial; TOML's are rare.