RelicRELIC
Developer cheat sheets

TOML vs YAML

TOML and YAML both describe configuration in plain text, but they were designed for different jobs. TOML aims to be obvious and minimal. YAML aims to serialize any data structure a human can read. That difference explains where each one won, and where YAML's famous sharp edges come from.

Jordan Gibbs July 10, 2026 7 min read

If you have edited a pyproject.tomland a Kubernetes manifest in the same week, you have used both. TOML (Tom’s Obvious, Minimal Language) and YAML (YAML Ain’t Markup Language) overlap enough that people ask which to pick, but they were built with different goals, and matching the format to the goal settles most arguments.

Design goals

  • TOML was designed to be an obvious, minimal config format that maps cleanly to a hash table. The priority is that a human can read it at a glance and rarely be surprised by how a value parses.
  • YAML was designed as a human-friendly data serialization language. It can represent arbitrary nested data, references, and multiple documents in one file, which makes it powerful and also gives it more ways to trip you up.

The same config in both

Here is a small service config written in TOML.

config.toml
title = "My Service"
port = 8080
debug = false

[database]
host = "localhost"
port = 5432
name = "app"

[[server]]
region = "us-east"
weight = 10

[[server]]
region = "eu-west"
weight = 5

And the same thing in YAML.

config.yaml
title: My Service
port: 8080
debug: false

database:
  host: localhost
  port: 5432
  name: app

server:
  - region: us-east
    weight: 10
  - region: eu-west
    weight: 5

Notice the trade already. TOML makes structure explicit with headers like [database] and [[server]], which reads clearly but gets verbose as nesting deepens. YAML uses indentation, which is compact for nested data but means whitespace is now load-bearing.

YAML’s sharp edges

YAML is genuinely pleasant for nested data, but it has a few traps that bite real projects. These are not folklore; they come from the spec and from parsers that never moved past an older version of it.

The Norway problem (implicit typing)

Under YAML 1.1, several unquoted words become booleans: yes, no, on, off, true, false. The classic bug is a list of country codes where NO (Norway) is silently read as the boolean false. YAML 1.2 fixed this by limiting booleans to true and false, but many widely used parsers kept the 1.1 behavior, so the problem is still out there. The defence is simple: quote any string that could be mistaken for another type.

Quote to be safe in YAML
countries:
  - "NO"
  - "SE"
  - "DK"
version: "1.10"

That version line matters too. Unquoted, 1.10 can be read as a number and lose its trailing zero, becoming 1.1.

Indentation and implicit typing in general

  • Whitespace defines structure, so a stray space or a tab where spaces are expected changes the meaning or fails the parse. Tabs for indentation are not allowed at all.
  • Implicit typing goes beyond booleans. A value that looks like a number, a date, or null gets coerced unless you quote it, which is how a ZIP code or a Git SHA can lose leading zeros or change type.
A blunt way to stay safe in YAML: when in doubt, quote the value. Strings that could be read as booleans, numbers, dates, or null are exactly the ones that cause silent bugs, and quoting them costs nothing.

TOML’s limits

TOML is not free of trade-offs either. Its strengths fade as data gets deeply nested.

  • Deep nesting gets noisy. Every level of nesting wants its own [section.subsection] header, and once you are three or four levels down, the headers repeat and the file gets long.
  • Arrays of tables read oddly at first. The [[server]] double-bracket syntax for a list of objects is unusual until you have seen it a few times, and it is easy to confuse with a single [server] table.

For flat or moderately nested config, none of this bites. TOML stays readable precisely because it refuses to be clever, which is the whole point of the format.

Where each one won

FormatWins forBecause
TOMLpyproject.toml, Cargo.tomlFlat-to-moderate config that a person edits by hand; minimal surprises
YAMLKubernetes, CI configs, Docker ComposeDeeply nested data and lists where indentation reads cleaner than headers
JSONData between programs, APIsStrict, unambiguous types every language parses the same way

The ecosystems reflect this. Rust and modern Python packaging standardized on TOML because the config is human-edited and mostly shallow. The cloud-native world runs on YAML because manifests and pipelines are deeply nested and change often. Neither community picked wrong; they picked for the shape of their data.

JSON, the third option

When a format is read by programs more than people, JSON is often the right answer. Its type rules are strict, every language parses it identically, and there is no implicit typing to surprise you. The costs are that it has no comments and is fussy about trailing commas, which makes it a poor format for config a human maintains. A common split is JSON on the wire and TOML or YAML for the file someone edits. If you are staring at a wall of unformatted JSON, our JSON formatter will indent and validate it in your browser.

Picking one

Match the format to the data. Shallow, hand-edited config: TOML. Deeply nested manifests and pipelines: YAML, with values quoted whenever they could be misread. Machine-to-machine data: JSON. For more references cut the same way, the regex cheat sheet and the rest of our developer cheat sheets keep the syntax you half-remember close at hand.

Frequently asked questions

Is TOML or YAML better for configuration?

It depends on the shape of the data. TOML is built to be an obvious, minimal config format, so it wins for flat-to-moderate settings like pyproject.toml or Cargo.toml. YAML is a general data serialization format that handles deep nesting and lists more gracefully, which is why Kubernetes manifests, CI pipelines, and Docker Compose files use it. If your config is shallow, TOML is easier to read and harder to break. If it is deeply nested, YAML reads better.

What is the Norway problem in YAML?

In YAML 1.1, unquoted values like no, yes, on, and off are parsed as booleans. So a list of country codes that includes NO for Norway can turn into the boolean false. YAML 1.2 fixed this by restricting booleans to true and false, but many popular parsers still follow 1.1 behavior, so the trap persists in practice. The fix is to quote strings that could be misread as another type.

Should I use JSON instead of TOML or YAML?

JSON is the safest choice for data exchanged between programs, because its type rules are strict and unambiguous and every language can parse it. It is a poor config format for humans because it has no comments and is picky about trailing commas. Many teams use JSON on the wire and TOML or YAML for the config a person edits by hand.

Written by
Jordan GibbsFounder, Relic

Jordan Gibbs is the founder of Relic, an end-to-end encrypted, permanent, searchable memory for everything you copy. He writes widely about AI, agents, and practical tooling on Medium, where he is read by tens of thousands, and builds privacy-first software. Here he covers how everyday tools like the clipboard actually work, and how to use them without handing your data to someone else.

MediumGitHubLinkedIn
Part of
Developer cheat sheets
Keep reading
Pillar·9 min

Regex cheat sheet

The regex pieces you actually reach for, character classes through lookarounds, with the flavor differences that bite you and a handful of patterns worth saving.

Read
8 min

JSONPath examples

JSONPath is XPath for JSON. One small store document, threaded through wildcards, slices, filters and recursive descent, with the honest note on how implementations vary.

Read
8 min

jq cheat sheet

jq is sed for JSON. The filters you use most, built up against one API response so every command has a visible result, from map and select to group_by and raw output.

Read
developerreferenceconfiguration