TOML Explained: The Config Format That's Replacing YAML
If you've worked with Rust, modern Python packaging, or Hugo, you've encountered TOML — Tom's Obvious, Minimal Language. It's a configuration file format designed to be easy to read, unambiguous to parse, and impossible to get wrong in the subtle ways that YAML constantly trips people up.
TOML has been quietly gaining ground as the configuration format of choice for developer tools. Rust chose it for Cargo.toml. Python adopted it for pyproject.toml (PEP 518/PEP 621). Hugo, the static site generator, uses it. And the reasons why tell you a lot about what's wrong with YAML.
TOML Syntax in 5 Minutes
TOML looks like an INI file that grew up to support modern data types:
# This is a comment
title = "My Project"
version = "1.0.0"
enabled = true
max_retries = 3
pi = 3.14159
# Dates are first-class
created = 2026-03-09T10:30:00Z
# Arrays
tags = ["rust", "config", "toml"]
# Tables (objects/maps)
[database]
host = "localhost"
port = 5432
name = "myapp"
# Nested tables
[database.connection_pool]
min = 5
max = 20
timeout = 30
# Array of tables
[[servers]]
name = "alpha"
ip = "10.0.0.1"
role = "primary"
[[servers]]
name = "beta"
ip = "10.0.0.2"
role = "replica"
That's it. No indentation rules, no special characters that change meaning based on context, no "Norway problem." Every TOML file is unambiguously parseable.
Why TOML Over YAML?
YAML is the most popular configuration format, but it has well-documented problems:
The Norway Problem
# YAML
countries:
- Norway # Parsed as boolean false in YAML 1.1
- France
- DE # Also parsed as boolean in some parsers
# TOML — no ambiguity
countries = ["Norway", "France", "DE"]
YAML 1.1 treats NO, no, Off, off, False, false (and their uppercase variants) as boolean false. Country codes like NO (Norway) and DE (Germany) can silently become booleans. YAML 1.2 fixed this, but many parsers still use YAML 1.1 semantics.
Indentation Sensitivity
# YAML — wrong indentation changes the structure
server:
host: localhost
port: 5432
name: myapp # One extra space = parse error or wrong nesting
# TOML — explicit structure, no indentation rules
[server]
host = "localhost"
port = 5432
name = "myapp"
Type Coercion Surprises
# YAML
version: 1.0 # Float, not string "1.0"
version: 1.10 # Float 1.1, not string "1.10"
phone: 12345678 # Integer, not string
# TOML — explicit types, no guessing
version = "1.0"
phone = "12345678"
In TOML, strings require quotes, numbers are numbers, booleans are true/false, and there's no automatic type coercion. You always know exactly what type a value is.
TOML vs JSON for Configuration
JSON works for configuration but has practical limitations:
- No comments — JSON doesn't support comments, which is a dealbreaker for configuration files that need inline documentation
- Trailing commas — JSON doesn't allow them, making editing error-prone
- Verbose for nested structures — lots of braces and brackets for deeply nested configs
- No date/time type — dates are just strings in JSON
TOML was explicitly designed to be a better JSON for config files: it has comments, dates, multi-line strings, and a syntax optimized for human readability.
Where TOML Is Used
| Project | File | Why TOML |
|---|---|---|
| Rust / Cargo | Cargo.toml | Package metadata, dependencies, build config |
| Python (PEP 518/621) | pyproject.toml | Project metadata, build system, tool config |
| Hugo | hugo.toml | Site configuration |
| Deno | deno.json (supports TOML) | Runtime configuration |
| Pipenv | Pipfile | Python dependency management |
| Black (Python formatter) | pyproject.toml | Formatter settings |
| Ruff (Python linter) | pyproject.toml | Linter configuration |
Complete TOML Syntax Reference
Strings
# Basic strings (escape sequences work)
name = "Alice \"Wonderland\" Smith"
path = "C:\\Users\\alice"
# Literal strings (no escaping — use single quotes)
regex = 'C:\Users\.*'
winpath = 'C:\Users\alice'
# Multi-line basic strings
description = """
This is a
multi-line string.
Newlines are preserved."""
# Multi-line literal strings
code = '''
fn main() {
println!("Hello, world!");
}
'''
Numbers
integer = 42
negative = -17
hex = 0xDEADBEEF
octal = 0o755
binary = 0b11010110
float = 3.14
scientific = 6.022e23
infinity = inf
not_a_number = nan
# Underscores for readability
large = 1_000_000
hex_color = 0xFF_AA_00
Dates and Times
# Offset date-time (RFC 3339)
created = 2026-03-09T10:30:00Z
updated = 2026-03-09T10:30:00-05:00
# Local date-time (no timezone)
meeting = 2026-03-09T14:00:00
# Local date
birthday = 1990-05-15
# Local time
alarm = 07:30:00
Tables and Nesting
# Standard table
[package]
name = "my-project"
version = "0.1.0"
# Dotted keys (inline nesting)
[package.metadata]
homepage = "https://example.com"
# Inline tables (for short tables)
point = { x = 1, y = 2 }
Arrays of Tables
# Each [[products]] creates a new element in an array
[[products]]
name = "Hammer"
price = 9.99
[[products]]
name = "Nail"
price = 0.05
# Equivalent JSON:
# {"products": [
# {"name": "Hammer", "price": 9.99},
# {"name": "Nail", "price": 0.05}
# ]}
Converting Between TOML and JSON
The mapping between TOML and JSON is straightforward:
# TOML
[server]
host = "localhost"
port = 5432
tags = ["primary", "production"]
[server.ssl]
enabled = true
cert = "/etc/ssl/cert.pem"
// Equivalent JSON
{
"server": {
"host": "localhost",
"port": 5432,
"tags": ["primary", "production"],
"ssl": {
"enabled": true,
"cert": "/etc/ssl/cert.pem"
}
}
}
Use our TOML Formatter to format TOML files and convert between TOML and JSON bidirectionally.
TOML's Limitations
TOML isn't perfect for every use case:
- Deeply nested structures get verbose — if your config is 5+ levels deep, the table header syntax becomes repetitive.
- No schema language — unlike JSON Schema, there's no standardized way to validate TOML structure (though tools like
taploprovide validation). - Less ecosystem support — JSON and YAML have parsers in every language. TOML support is good but not universal.
- Not great for data interchange — TOML is designed for configuration, not for APIs or data serialization. Use JSON for that.
When to Use What
| Use Case | Best Format | Why |
|---|---|---|
| Application config | TOML | Readable, typed, comments, no ambiguity |
| API responses | JSON | Universal support, fast parsing |
| Kubernetes manifests | YAML | Ecosystem requirement (no choice) |
| Docker Compose | YAML | Ecosystem requirement |
| CI/CD config | YAML | GitHub Actions, GitLab CI require it |
| Package metadata | TOML | Cargo.toml, pyproject.toml set the standard |
Need to format or convert TOML? Try our TOML Formatter & Converter — format, minify, and convert between TOML and JSON, all in your browser.