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.