YAML vs JSON vs TOML: Which Config Format Should You Use?
YAML, JSON, and TOML are the three dominant formats for configuration files and structured data in modern software. They overlap significantly — all three can represent the same data — but each has a different philosophy about readability, strictness, and use case. Choosing the wrong one causes unnecessary friction.
The Same Data in All Three Formats
# YAML
server:
host: localhost
port: 8080
debug: true
database:
url: postgres://localhost/mydb
pool_size: 10
timeout: 30
features:
- auth
- analytics
- billing// JSON
{
"server": {
"host": "localhost",
"port": 8080,
"debug": true
},
"database": {
"url": "postgres://localhost/mydb",
"pool_size": 10,
"timeout": 30
},
"features": ["auth", "analytics", "billing"]
}# TOML
[server]
host = "localhost"
port = 8080
debug = true
[database]
url = "postgres://localhost/mydb"
pool_size = 10
timeout = 30
features = ["auth", "analytics", "billing"]Feature Comparison
| Feature | YAML | JSON | TOML |
|---|---|---|---|
| Comments | ✅ Yes (#) | ❌ No | ✅ Yes (#) |
| Trailing commas | N/A | ❌ Not allowed | N/A |
| Multiline strings | ✅ Block scalars (| and >) | ⚠️ With \n escape | ✅ Triple-quoted strings |
| Indentation-sensitive | ✅ Yes (spaces only) | ❌ No | ❌ No |
| Explicit types | ⚠️ Implicit (with gotchas) | ✅ Yes | ✅ Yes |
| Native date type | ✅ Yes | ❌ No (string) | ✅ Yes (RFC 3339) |
| Anchors / aliases | ✅ Yes (&anchor, *alias) | ❌ No | ❌ No |
| Multi-document files | ✅ Yes (---) | ❌ No | ❌ No |
| Spec complexity | ⚠️ Very complex (80+ pages) | ✅ Simple | ✅ Moderate |
| Human writability | ✅ Good (once learned) | ⚠️ OK (verbose) | ✅ Very good |
| Machine readability | ✅ Good | ✅ Excellent | ✅ Good |
When to Use YAML
YAML is the right choice when configuration is written by humans, deeply nested, and benefits from comments. It's the dominant format in the DevOps and cloud-native ecosystem:
- Kubernetes — all resource manifests (Deployments, Services, ConfigMaps)
- Docker Compose —
docker-compose.yml - GitHub Actions — workflow files in
.github/workflows/ - GitLab CI —
.gitlab-ci.yml - Ansible — playbooks and inventory files
- Helm — chart values and templates
YAML's anchors and aliases (&anchor / *alias) let you reuse values across the file — useful in large CI configs where many jobs share the same base configuration.
# YAML anchors — reuse a base configuration
defaults: &defaults
image: node:20
timeout: 30
retry: 2
build:
<<: *defaults # merge all keys from defaults
script: npm run build
test:
<<: *defaults
script: npm testGotcha: YAML uses indentation for structure — tabs are not allowed, only spaces. A single misplaced space can change the meaning or cause a parse error. Always validate YAML before deploying it.
When to Use JSON
JSON is the right choice when the data is machine-generated, consumed by an API, or needs to be universally parseable:
- REST APIs — request and response bodies
- package.json — Node.js project manifest
- tsconfig.json — TypeScript configuration (supports comments via JSONC)
- GeoJSON, OpenAPI specs — standardized data exchange formats
- Database storage — JSON columns in PostgreSQL, MySQL
- Configuration when schema validation is important — JSON Schema is the most mature tooling
JSON's biggest advantage is universal support — every programming language has a built-in JSON parser. Its biggest weakness is verbosity (all strings must be quoted, no comments) and strict syntax (no trailing commas).
When to Use TOML
TOML is the right choice when you need a human-friendly, unambiguous format for application configuration:
- Rust —
Cargo.toml(project manifest and dependencies) - Python —
pyproject.toml(packaging, linting, testing config) - Hugo — static site configuration
- Gitea, Forgejo — server configuration
- Any new project where you control the config format
TOML is explicitly typed, non-indentation-sensitive, and has no implicit type conversion surprises. It maps cleanly to a hash table and is easy to parse. The section-based syntax [section] and [[array_of_tables]] is intuitive for flat-to-moderate nesting.
# TOML — explicit types, no indentation required
[package]
name = "my-app"
version = "1.0.0"
authors = ["Alice <alice@example.com>"]
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
[[servers]]
name = "production"
host = "prod.example.com"
port = 443
[[servers]]
name = "staging"
host = "staging.example.com"
port = 8443The YAML Norway Problem
One of YAML 1.1's most infamous quirks: unquoted NO was parsed as the boolean false, causing country: NO (Norway's ISO code) to become country: false. Similarly, yes, on,off, true, false — and their capitalized variants — were all treated as booleans.
# YAML 1.1 (PyYAML, many older tools) — dangerous implicit conversions
country: NO # parsed as false ← the Norway problem
enabled: yes # parsed as true
debug: on # parsed as true
value: 1_000_000 # parsed as 1000000 (octal-like underscores)
version: 1.0 # parsed as float, not string
# Fix: always quote values that could be misinterpreted
country: "NO"
enabled: "yes"
version: "1.0"YAML 1.2 (2009) removed most of these implicit conversions, but many tools (including PyYAML until version 6.0) still use YAML 1.1 parsing by default. Always quote string values that look like booleans, numbers, or null.
Quick Decision Guide
| Situation | Use |
|---|---|
| API request/response body | JSON |
| Node.js project manifest | JSON (package.json) |
| TypeScript config | JSON/JSONC (tsconfig.json) |
| Kubernetes / Docker / CI pipeline | YAML |
| Ansible playbooks | YAML |
| Rust project config | TOML (Cargo.toml) |
| Python project config | TOML (pyproject.toml) |
| Static site config (Hugo) | TOML |
| New project — you choose the format | TOML (safest) or YAML (if deeply nested) |
| Schema-validated config | JSON (best tooling support) |
Need to validate or convert between formats? YAML Validator · JSON Formatter · YAML to JSON converter
Frequently Asked Questions
Can YAML parse JSON?▾
Yes — valid JSON is also valid YAML, since YAML 1.2 is a superset of JSON. A YAML parser can read a JSON file without any changes. This is useful in systems that support YAML configuration: you can write your config as JSON if you prefer, and the YAML parser will handle it. The reverse is not true: YAML files often use syntax (anchors, multiline strings, unquoted values) that JSON parsers cannot read.
Why does YAML have a reputation for being error-prone?▾
Several reasons. First, YAML uses indentation for structure, so a misplaced space causes a parse error or silently changes the data. Second, YAML has many implicit type conversions: 'yes', 'on', 'true', 'True' all become the boolean true in YAML 1.1 (common in older tools like PyYAML). 'Norway problem': the country code 'NO' was parsed as false. Third, YAML's spec is very complex — over 80 pages — with many edge cases. YAML 1.2 fixed most of the implicit conversion issues, but many tools still use 1.1 parsers.
What is the difference between TOML and INI format?▾
TOML is inspired by INI but is more structured. INI has no standard spec and no support for nested data, arrays, or typed values — everything is a string. TOML has a formal specification, supports typed values (integers, floats, booleans, dates, arrays, inline tables), and supports nested tables. If you've outgrown INI and want something human-friendly without YAML's indentation traps, TOML is the natural upgrade.
Why does Kubernetes use YAML instead of JSON or TOML?▾
Kubernetes manifests started as JSON (the API is JSON-based) but YAML became the preferred format for human-authored config because it's less noisy — no closing braces, no quotes on most strings, and comments are supported. YAML's support for multi-document files (multiple resources separated by ---) also fits Kubernetes well. The downside is that YAML's indentation sensitivity causes many user errors, which is why tools like Helm and kustomize exist to generate YAML programmatically.
Is TOML better than YAML for configuration?▾
For many use cases, yes. TOML has less syntax ambiguity, explicit types, a simpler spec, and no indentation-sensitive structure. It's especially well-suited to flat or moderately nested configs like Cargo.toml, pyproject.toml, or Hugo config files. YAML remains better for deeply nested data (like Kubernetes manifests) and for systems where the config is primarily generated by machines rather than written by hand. If you're choosing between them for a new project's config format, TOML is usually the safer pick for human-authored files.