712Tools
5 min read

TOML vs YAML vs JSON: picking a config format in 2026

Rust picked TOML. Python packaging picked TOML. Cloudflare Workers picked TOML. Here's why the config landscape shifted, and how to move between TOML, YAML, and JSON without breakage.

By
Software Engineer ยท M.Sc. Mechanical Engineering ยท Ontario, Canada

Why TOML is quietly winning

Five years ago, YAML was the default config format for anything longer than .env. In 2026 the picture has shifted: TOML runs Rust (Cargo.toml), Python packaging (pyproject.toml), Cloudflare Workers (wrangler.toml), the Zig build system, Poetry, and countless CLIs.

The pitch: TOML is what YAML was supposed to be โ€” human-friendly, unambiguous, easy to write. Without YAML's traps (the Norway problem, indent-sensitivity, silent type coercion, six versions of multi-line strings).

The three-format decision matrix

Feature JSON YAML TOML
Comments โœ— โœ“ โœ“
Trailing commas โœ— n/a โœ“ (in arrays)
Multi-line strings โœ— (escape \n) โœ“ (three styles) โœ“ (""")
Indent-sensitive โœ— โœ“ โœ—
Boolean coercion pitfalls โœ— โœ“ (yes/no/on/off) โœ—
Anchors / references โœ— โœ“ โœ—
Nested structures Free-form Free-form Table-based
Machine-only interchange โœ“โœ“ โ€” โ€”
Human-authored config โ€” โœ“ โœ“โœ“

JSON wins when machines write and machines read (APIs, RPC). YAML wins when you need anchors/refs (Kubernetes, some CI systems). TOML wins for human-authored config โ€” the tables model matches how people mentally group settings.

The TOML mental model

Everything is a table (equivalent to a JSON object). The root is a table. [section] opens a new table. [[section]] starts an array-of-tables. That's the whole structure.

# Root-level values
name = "712tools"
version = "1.0"

# A named table
[owner]
name = "Ada"
email = "ada@example.com"

# An array-of-tables (like a JSON array of objects)
[[servers]]
host = "us-east.example.com"
region = "us-east-1"

[[servers]]
host = "eu-west.example.com"
region = "eu-west-1"

Every key has one type. Strings are quoted; numbers, booleans, and dates are bare. Arrays use []. Inline tables use {} for compact one-liners.

That's the full spec worth memorizing. No indent traps, no anchor magic.

When TOML gets ugly

Deeply nested structures. TOML tables model 1-2 levels of nesting cleanly; 4+ levels start to feel awkward because you keep repeating the full dotted path:

[services.web.upstreams.primary]
host = "web-primary"

For anything heavily nested (Kubernetes manifests, complex CI pipelines), YAML or JSON reads better. Cargo.toml and pyproject.toml stay flat enough for TOML to shine.

The migration workflows

Migrating from JSON to TOML (e.g., converting an old config.json to a config.toml):

Convert with TOML โ†” JSON Converter. Every top-level object becomes a table, every nested object becomes a sub-table, every array-of-objects becomes an array-of-tables ([[...]]).

Migrating from YAML to TOML (rare but happens โ€” e.g., a new Rust rewrite of a Python service):

Two-step: YAML โ†’ JSON with YAML โ†” JSON Converter, then JSON โ†’ TOML. The intermediate JSON step drops YAML-specific features (anchors, complex multi-line strings) that don't have TOML equivalents.

Migrating from .env to TOML for structured secrets and config:

Two-step: .env โ†’ JSON with .env โ†” JSON Converter, then JSON โ†’ TOML. Group related keys into tables ([database], [redis]) for cleaner organization than the flat .env layout.

Cargo.toml, pyproject.toml, wrangler.toml โ€” the three you'll edit most

Cargo.toml (Rust):

[package]
name = "my-crate"
version = "0.1.0"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }

pyproject.toml (Python packaging, PEP 518+):

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "my-package"
version = "0.1.0"
dependencies = [
  "requests>=2.28",
  "pydantic>=2.0",
]

wrangler.toml (Cloudflare Workers):

name = "my-worker"
main = "src/index.ts"
compatibility_date = "2026-08-17"

[[kv_namespaces]]
binding = "CACHE"
id = "abc123"

All three use the same core syntax. Learn TOML once, edit all three fluently.

Common TOML mistakes

1. Extra quotes on numbers. port = "3000" parses as a string. TOML has actual number types โ€” port = 3000.

2. Missing quotes on strings. name = 712tools parses as an identifier in some parsers and fails in others. Quote it: name = "712tools".

3. Confusing [table] with [[array-of-tables]]. Single brackets = one table. Double brackets = one entry in an array of tables. Getting this wrong changes the parsed structure entirely.

4. Deep nesting via dotted keys. a.b.c = 1 is legal but hard to read. Prefer a table:

[a.b]
c = 1

5. Multi-line strings preserving indentation. Basic multi-line strings (""") preserve every space including leading indent. Use trimming ("""\) or the literal form (''') as appropriate.

Related workflows

Tools mentioned in this post

Written by Shan

Shan builds 712 Tools. He holds a Master's degree in Mechanical Engineering and now works as a Software Engineer, shipping browser-based developer utilities out of Ontario, Canada. Learn more ยท 712studiogames@gmail.com