Configuration management sits at the intersection of human usability and automated tooling. While JSON dominates machine-to-machine data interchange and YAML anchors cloud-native infrastructure manifests (such as Kubernetes and GitHub Actions), TOML (Tom's Obvious Minimal Language) has emerged as the premier standard for project configuration files and build systems.
Standardized under the formal TOML v1.0.0 specification, TOML powers Rust's package ecosystem (Cargo.toml), modern Python packaging standards (pyproject.toml per PEP 518 and PEP 621), Hugo static site generators, Containerd daemon configurations, and Deno toolchains.
This guide explores TOML's underlying grammar, contrasts single tables against arrays of tables ([[table]]), examines inline table constraints, demonstrates programmatic parsing across four major runtimes, and covers lossless conversion between TOML and JSON.
1. TOML Core Grammar & Structural Hierarchy
Unlike YAML, which enforces hierarchy through significant indentation and whitespace indentation rules, TOML enforces hierarchy through explicit table headers enclosed in brackets and dot-separated key paths. Indentation in TOML is purely cosmetic.
# Top-level key-value primitives
title = "DevFlow API Gateway"
release_date = 2026-09-06T12:00:00Z
is_production = true
worker_threads = 8
request_timeout_ms = 4500
# Sub-table (Dictionary / Hash Table)
[server]
host = "0.0.0.0"
port = 8080
max_body_size_mb = 50
# Nested Sub-table via Dot Notation
[server.tls]
enabled = true
min_version = "1.3"
cert_file = "/etc/ssl/certs/gateway.crt"
key_file = "/etc/ssl/private/gateway.key"
┌────────────────────────────────────────────────────────────────────────┐
│ TOML Document Structural Model │
│ │
│ Root Document (Global Scope) │
│ ├─ Key-Value Pairs (title, release_date, is_production) │
│ │ │
│ ├─ Table: [server] │
│ │ ├─ Keys: host, port, max_body_size_mb │
│ │ │ │
│ │ └─ Sub-Table: [server.tls] │
│ │ └─ Keys: enabled, min_version, cert_file, key_file │
│ │ │
│ └─ Array of Tables: [[upstream_clusters]] │
│ ├─ Entry 1: { name = "auth", host = "10.0.1.1", port = 4001 } │
│ └─ Entry 2: { name = "billing", host = "10.0.1.2", port = 4002 } │
└────────────────────────────────────────────────────────────────────────┘
Bare Keys vs. Quoted Keys
Keys in TOML are separated into two categories:
- Bare Keys: Contain only ASCII letters (
A-Za-z), ASCII digits (0-9), underscores (_), and hyphens (-).app-name = "billing" retry_count = 3 - Quoted Keys: Enclosed in double (
"...") or single ('...') quotes. Mandatory when keys contain spaces, dots, colons, slashes, non-ASCII Unicode characters, or symbols."127.0.0.1" = "localhost" "Content-Type" = "application/json" "service:cache" = { enabled = true }
2. Single Tables ([table]) vs. Arrays of Tables ([[table]])
One of the most frequent points of confusion for developers transitioning to TOML is the distinction between single brackets [...] and double brackets [[...]].
Single Tables: [database] (JSON Object)
A single bracket header defines a unique dictionary / object. Defining the same table header twice in a single document causes a fatal parse error.
[database]
host = "postgres.internal"
port = 5432
Equivalent JSON:
{
"database": {
"host": "postgres.internal",
"port": 5432
}
}
Arrays of Tables: [[servers]] (JSON Array of Objects)
Double bracket headers denote an element in a sequence of tables. Every occurrence of [[servers]] instantiates a new dictionary appended to the servers array.
[[servers]]
name = "primary-db"
ip = "10.0.1.10"
role = "leader"
[[servers]]
name = "replica-db-01"
ip = "10.0.1.11"
role = "follower"
Equivalent JSON:
{
"servers": [
{
"name": "primary-db",
"ip": "10.0.1.10",
"role": "leader"
},
{
"name": "replica-db-01",
"ip": "10.0.1.11",
"role": "follower"
}
]
}
3. Inline Tables vs. Standard Sections
TOML provides Inline Tables ({ ... }) for concise, single-line object definitions.
# Standard Section Table
[dependencies]
serde = { version = "1.0.200", features = ["derive"] }
tokio = { version = "1.38", features = ["full", "tracing"] }
axum = "0.7"
Critical Rules for Inline Tables:
- Compact & Single-Line: Inline tables must be declared on a single line; raw unescaped newlines between key-value pairs are forbidden.
- Immutability: Once an inline table is declared, it is closed and sealed. You cannot add additional child properties using dotted key paths later in the file:
# ❌ ILLEGAL under TOML v1.0.0 (causes parse error) database = { host = "localhost" } database.port = 5432
4. Native Primitive Data Types
TOML offers richer primitive type representations than standard JSON:
| Data Type | TOML Syntax Example | Description |
|---|---|---|
| String (Basic) | "Hello\nWorld" |
Escaped UTF-8 with standard \n, \t, \uXXXX sequences |
| String (Literal) | 'C:\Users\app\file.txt' |
Raw string with zero escape interpretation (ideal for regex/paths) |
| Multiline Basic | """Line 1\nLine 2""" |
Multi-line string with backslash escaping and trimming |
| Multiline Literal | '''Raw\nText\NoEscapes''' |
Raw multi-line string preserving exact characters |
| Integer | 42, -17, 1_000_000, 0xDEADBEEF, 0o755, 0b1101 |
Decimal, Hex, Octal, Binary with digit separator _ |
| Float | 3.1415, -0.01, 1e6, inf, -inf, nan |
IEEE 754 64-bit floating-point numbers |
| Boolean | true, false |
Lowercase boolean constants |
| Offset Date-Time | 2026-09-06T15:30:00Z, 2026-09-06T11:30:00-04:00 |
Full RFC 3339 timestamp with timezone offset |
| Local Date-Time | 2026-09-06T15:30:00 |
Date-time without timezone locality |
| Local Date | 2026-09-06 |
Calendar date (YYYY-MM-DD) |
| Local Time | 15:30:00.500 |
Wall-clock time (HH:MM:SS[.fractional]) |
| Array | [ 1, 2, 3 ], [ "a", "b", "c" ] |
Sequence of values (heterogeneous types permitted in v1.0.0) |
5. Real-World Manifests: Rust Cargo.toml & Python pyproject.toml
Rust Cargo.toml Manifest Architecture
Rust's cargo build system leverages TOML tables for package metadata, conditional compilation features, dependency trees, and target artifacts:
[package]
name = "devflow-worker"
version = "2.4.0"
edition = "2021"
authors = ["DevFlow Core <[email protected]>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.38", features = ["macros", "rt-multi-thread"] }
smol-toml = "1.2"
tracing = "0.1"
[features]
default = ["std"]
std = []
simd = []
[[bin]]
name = "worker-daemon"
path = "src/main.rs"
To auto-generate typed Rust data models for your configuration schemas, check out our JSON to Rust Struct Converter.
Python pyproject.toml (PEP 518 & PEP 621)
Modern Python packaging unifies build backends, package metadata, dependencies, and tool settings in a single declarative TOML file:
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[project]
name = "api-gateway"
version = "0.5.0"
description = "High-speed async routing gateway"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.111.0",
"pydantic>=2.7.0",
"uvicorn[standard]>=0.30.0",
"httpx>=0.27.0",
]
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
[tool.pytest.ini_options]
minversion = "8.0"
addopts = "-ra -q --strict-markers"
testpaths = ["tests"]
To generate Pydantic v2 validation models from your configuration schemas, use our JSON to Pydantic Model Generator.
6. Programmatic TOML Parsing Across Languages
Node.js / TypeScript (smol-toml)
smol-toml is a fast, zero-dependency, spec-compliant TOML v1.0.0 parser and serializer for modern JavaScript environments:
import { parse, stringify } from 'smol-toml';
const rawToml = `
[database]
server = "127.0.0.1"
port = 5432
`;
// Parse TOML into JavaScript Object
const config = parse(rawToml);
console.log(`Database Port: ${config.database.port}`);
// Serialize Object to TOML
const tomlOutput = stringify({
service: {
name: "auth-gateway",
active: true,
},
});
Python 3.11+ Standard Library (tomllib)
Starting with Python 3.11, TOML parsing is built directly into the standard library:
import tomllib
# tomllib requires reading in binary mode ('rb')
with open("pyproject.toml", "rb") as f:
project_config = tomllib.load(f)
print("Project Name:", project_config["project"]["name"])
print("Dependencies:", project_config["project"]["dependencies"])
Rust (toml + serde)
In Rust, pair the toml crate with serde for compile-time typed deserialization:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
package: PackageConfig,
}
#[derive(Debug, Deserialize)]
struct PackageConfig {
name: String,
version: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let toml_str = r#"
[package]
name = "devflow"
version = "1.0.0"
"#;
let config: Config = toml::from_str(toml_str)?;
println!("Crate: {} v{}", config.package.name, config.package.version);
Ok(())
}
Go (pelletier/go-toml/v2)
In Go, deserialize TOML into typed structs using struct field tags:
package main
import (
"fmt"
"github.com/pelletier/go-toml/v2"
)
type Config struct {
Server struct {
Host string `toml:"host"`
Port int `toml:"port"`
} `toml:"server"`
}
func main() {
doc := []byte(`
[server]
host = "0.0.0.0"
port = 8080
`)
var cfg Config
if err := toml.Unmarshal(doc, &cfg); err != nil {
panic(err)
}
fmt.Printf("Server listening on %s:%d\n", cfg.Server.Host, cfg.Server.Port)
}
7. Converting Between TOML, JSON, and YAML
When integrating heterogeneous systems—such as converting a pyproject.toml into a JSON schema, or transforming a JSON REST response into a Hugo site parameter file—converting between configuration formats is a daily requirement.
To convert and inspect your configuration files instantly with zero server round-trips and 100% in-browser privacy:
- TOML Converter — Convert between TOML and JSON with automated syntax validation and key sorting.
- YAML Converter — Convert between YAML and JSON for Kubernetes manifests and GitHub Actions workflows.
- JSON Formatter & Validator — Inspect, format, and validate complex JSON object trees.
- Env File Parser & Converter — Convert
.envfiles into JSON, TOML, and Docker environment variables. - Text Diff Checker — Compare configuration files side-by-side to verify serialization parity.