JSON vs YAML vs TOML: Which Config Format Should You Use in 2026
A team I worked with once spent an entire afternoon debugging a Kubernetes manifest because someone wrote country: NO and YAML quietly converted Norway's country code into the boolean false. That's not a hypothetical edge case—that's a real category of bug that's killed deployments and broken production systems.
JSON, YAML, and TOML look interchangeable on the surface. They all store key-value data, they all support nesting, and most developers treat them as just "config stuff." But their syntax models, tooling ecosystems, and hidden gotchas are all fundamentally different. Pick wrong and you'll be debugging whitespace at midnight.
The Design Philosophy Behind Each Format
These three formats weren't created to do the same thing—they were created for different audiences and different priorities.
JSON: The Machine Wire Format
JSON (JavaScript Object Notation) was extracted from JavaScript in 2001 as a wire format for APIs. Its priority is unambiguous machine parsing. Every language ships a JSON parser in its standard library. The spec (RFC 8259) fits in 16 pages. There's no comment syntax, no trailing commas allowed, no type ambiguity.
That minimalism is the feature, not a limitation. JSON is the format when two systems need to agree on data without debate. It's the lingua franca of HTTP APIs, and it's not going anywhere.
YAML: The Human-Readable Configuration
YAML (YAML Ain't Markup Language) emerged around the same time with a different goal: human readability and editability. Indentation is meaningful, comments are allowed, and the same data structures as JSON can be expressed with less syntactic noise. The YAML 1.2 spec is technically a superset of JSON—but the YAML world rarely leverages that fact directly.
YAML is ideal when humans will read and edit the file frequently, especially for complex nested configurations. Think Kubernetes manifests, Docker Compose files, GitHub Actions workflows.
TOML: The Unambiguous Config Language
TOML (Tom's Obvious, Minimal Language) was created by GitHub co-founder Tom Preston-Werner in 2013 as a reaction to YAML's complexity. The tagline is "config file format for humans"—but unlike YAML, it's flat, line-oriented, and unambiguous. No whitespace sensitivity, explicit syntax, clear sections.
Rust's Cargo, Python's pyproject.toml, and Hugo all picked TOML because it removes the footguns that make YAML frustrating. When your config is read by machines but written by humans, TOML often wins.
The Same Data in All Three Formats
Let's look at the same configuration—a web service setup—in all three formats. This is the best way to understand their visual differences.
JSON
{
"service": {
"name": "api-gateway",
"port": 8080,
"tls": true
},
"database": {
"host": "localhost",
"port": 5432,
"max_connections": 50
},
"tags": ["production", "us-east", "v2"]
} YAML
service:
name: api-gateway
port: 8080
tls: true
database:
host: localhost
port: 5432
max_connections: 50
tags:
- production
- us-east
- v2 TOML
[service]
name = "api-gateway"
port = 8080
tls = true
[database]
host = "localhost"
port = 5432
max_connections = 50
tags = ["production", "us-east", "v2"] Notice how YAML wins on character count for shallow structures—less visual noise. TOML wins on "where am I in this file"—every section header clearly delineates scope. JSON is verbose but foolproof to parse.
The Gotchas That Will Bite You
Each format has its traps. Here's what you need to know before you commit.
The Norway Problem (YAML)
YAML 1.1's implicit type coercion turns bare words into booleans or numbers. The most famous example:
countries:
- GB
- IE
- NO # Norway? Or boolean false?
- SE In YAML 1.1, NO, Yes, On, and Off all parse as booleans. YAML 1.2 removed most of these, but many parsers still default to 1.1 behavior—including older PyYAML and Ruby implementations. The fix is simple: quote your strings: "NO".
But it gets worse. Version numbers like 1.10 parse as 1.1 (a float, dropping the trailing zero). 09:00 may parse as a sexagesimal number depending on parser version. Dates, times, MAC addresses—all have type coercion edge cases. Always quote strings that might collide with YAML keywords.
The "No Comments" Limitation (JSON)
JSON has no comment syntax by design. Douglas Crockford, who created JSON, argued that comments encourage parsing directives that break interoperability. In practice, this is annoying for configuration files.
Many projects work around this with JSONC (JSON with Comments) or JSON5, but those are non-standard extensions. If you need comments in your config, JSON isn't the answer.
The Deep Nesting Awkwardness (TOML)
TOML's table syntax gets verbose for deeply nested structures:
[a]
[b]
[c]
[d]
value = "nested" Compare that to YAML's indentation or JSON's dot notation. For flat configurations with a few sections, TOML is elegant. For deeply hierarchical configs, it gets noisy.
The Billion Laughs Attack (YAML)
YAML's anchors and aliases feature is useful for DRY config but a known DoS vector. A malicious file can define recursive aliases that expand exponentially:
a: &a ["lol"]
b: &b [*a, *a]
c: &c [*b, *b]
d: [*c, *c, *c, *c] This "billion laughs" attack can make a tiny YAML file expand to gigabytes in memory during parsing. Most parsers now have safeguards, but it's a reminder that YAML's power features come with security considerations.
Feature Comparison Table
| Feature | JSON | YAML | TOML |
|---|---|---|---|
| Comments | ❌ No | ✅ Yes (#) | ✅ Yes (#) |
| Multi-line strings | ❌ Escaped \n | ✅ Native (| and >) | ✅ Native (""") |
| Whitespace sensitivity | ❌ No | ✅ Yes (indentation) | ❌ No |
| Type safety | ✅ Good | ❌ Limited (implicit) | ✅ Strong |
| Native dates | ❌ Strings only | ✅ Yes | ✅ Yes (RFC 3339) |
| Anchors/Aliases | ❌ No | ✅ Yes | ❌ No |
| Spec complexity | ~16 pages | ~80 pages | ~30 pages |
| Parsing speed | Very Fast | Slower | Fast |
| Ecosystem size | Massive | Large | Growing |
When to Use Each Format
Use JSON for...
- HTTP API payloads and responses
- Machine-to-machine data exchange
- Configuration where comments aren't needed
- package.json, tsconfig.json, and similar manifests
- Anything that crosses a network boundary
Use YAML for...
- Kubernetes manifests and Helm charts
- Docker Compose files
- CI/CD pipelines (GitHub Actions, GitLab CI)
- Configuration that humans edit frequently
- Complex nested structures with references
Use TOML for...
- Rust projects (Cargo.toml)
- Python projects (pyproject.toml)
- Static site generators (Hugo)
- Application configuration where humans edit files
- Build tool configuration
Performance Considerations
If parsing speed matters for your use case, here's the reality from 2025 benchmarks on a 5MB config file:
- JSON: ~80ms parse time, 70MB peak memory
- YAML (C library): ~600ms parse time, 180MB peak memory
- TOML: ~200ms parse time, 90MB peak memory
JSON wins at scale by an order of magnitude. YAML's flexibility costs CPU and RAM. For a 100-byte file, these differences are invisible. For a 50MB Kubernetes manifest pile, they decide whether your CI runs in 30 seconds or 3 minutes.
Converting Between Formats
Need to convert? Here's what you need to know:
- YAML → JSON: Works well, YAML is a superset
- JSON → YAML: Works, but key order becomes arbitrary
- TOML → JSON: Works, but TOML dates become strings
- Comments: Lost when converting TO JSON (adds up in YAML)
- Anchors/Aliases: Cannot be represented in JSON or TOML
If your pipeline converts formats, pin the canonical source. Don't let three formats coexist for the same data.
My Decision Framework
After years of picking the wrong format and paying for it in production bugs, here's how I decide:
- Does the ecosystem demand a specific format? Use Cargo.toml for Rust, pyproject.toml for Python, k8s manifests for Kubernetes. Fight the ecosystem and you'll fight forever.
- Is this going over a network? JSON. Always. No debate.
- Will humans edit this at 2am? TOML. No whitespace gotchas, no Norway problem.
- Is this deeply nested with complex references? YAML, but validate in CI to catch indentation bugs.
- Default choice for new projects? TOML for config files, JSON for data exchange.
Validate Before You Deploy
Whatever format you pick, validate it in CI before deployment. Syntax errors in config files are embarrassingly common and completely preventable.
For JSON work, ToolMixr's JSON Formatter validates syntax and beautifies your code so errors are obvious. It runs entirely in your browser—your JSON never leaves your device.
The Bottom Line
JSON, YAML, and TOML aren't interchangeable. JSON is the wire format for machine-to-machine traffic—no comments, no surprises, RFC 8259. YAML is the human-edited configuration format, powerful but riddled with traps (the Norway problem, implicit typing, multi-document files). TOML is the unambiguous config format that the Rust and Python tooling ecosystems standardized on, verbose for deep nesting but predictable.
Pick by use case: APIs and logs in JSON, Kubernetes and CI in YAML, language tooling in TOML. Validate everything in CI, quote your strings in YAML, and for the love of your sleep—validate before you deploy.