TOML#

TOML (Tom’s Obvious Minimal Language) is a text-based config format with a real spec. The de-facto choice for tooling configuration in 2026: pyproject.toml, Cargo.toml, uv.lock, poetry.lock, rustfmt.toml, rclone.conf, and most modern CLI configs. Key/value pairs grouped under bracketed sections; obvious mapping to a hash of hashes; types include strings, integers, floats, booleans, dates, arrays, and tables.

Designed to read clearly to a human and parse unambiguously by a machine. Solves the problems INI never standardised (typed values, nesting, arrays, datetimes) while staying easier to hand-edit than YAML.

Minimal example#

# comment
title = "TOML example"

[package]
name = "myscan"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1", features = ["full"] }
anyhow = "1"

[[bin]]
name = "scan"
path = "src/main.rs"

Types#

Every value has a type the parser determines from the literal form.

Type

Literal

string

"hello" (basic), 'no \n escapes' (literal), """multi-line""", '''multi-line literal'''

integer

42, 0xff, 0o755, 0b1010, 1_000_000

float

3.14, 6.022e23, inf, nan

boolean

true, false

offset datetime

2026-05-17T08:30:00Z, 2026-05-17T08:30:00+02:00

local datetime

2026-05-17T08:30:00

local date

2026-05-17

local time

08:30:00

array

[1, 2, 3], ["a", "b"]

table

[section] block or {inline = "form"}

port      = 8080
timeout   = 30.5
enabled   = true
created   = 2026-05-17T08:30:00Z
hosts     = ["a.example", "b.example"]
headers   = { "X-Trace" = "1", "X-Origin" = "scan" }

Tables#

A table is a key/value group. Declared with bracketed headers; nested with dotted names.

[database]
host = "127.0.0.1"
port = 5432

[database.pool]
min = 1
max = 20

[server.tls]
cert_file = "/etc/ssl/cert.pem"
key_file  = "/etc/ssl/key.pem"

Equivalent inline form.

database = { host = "127.0.0.1", port = 5432,
             pool = { min = 1, max = 20 } }

Inline tables must fit on one logical line and cannot be re-opened later in the file. The bracketed form is the default for anything beyond a few keys.

Arrays of tables#

Double brackets [[name]] declare an array of tables. Each occurrence appends a new table to the array.

[[user]]
name  = "alice"
roles = ["admin"]

[[user]]
name  = "bob"
roles = ["reader", "writer"]

[[user]]
name  = "operator"
roles = ["admin", "auditor"]

Parses to user = [{...}, {...}, {...}]. The canonical TOML pattern for “list of records” (dependencies, bin targets, features).

Strings#

Four forms.

Form

When

"basic"

escape sequences (\n, \t, \uXXXX) processed

'literal'

no escapes; what you see is what you get

"""multi-line"""

spans lines; escapes processed

'''multi-line literal'''

spans lines; no escapes

path        = 'C:\Users\op\config'        # backslashes safe
description = """
multi-line description
that spans lines.
"""

Trailing backslash on a multi-line string trims the newline that follows.

Dotted keys#

Dots inside keys nest tables without a bracketed header.

server.host = "0.0.0.0"
server.port = 8080
server.tls.cert = "cert.pem"

# equivalent to:
#   [server]
#   host = "0.0.0.0"
#   port = 8080
#
#   [server.tls]
#   cert = "cert.pem"

Useful for tiny configs; the bracketed form reads better past five lines.

TOML vs YAML vs JSON#

TOML

typed values, dates first-class, explicit sections, no indentation rules. Best for project / tool config.

YAML

terser, indentation-significant, anchors / aliases, reference cycles possible. Best for human-edited configs with reused fragments (CI pipelines, k8s).

JSON

no comments, no dates, no trailing commas, strict. Best for wire / machine-to-machine, not for hand edit.

Operators meet TOML when the project already picked it (pyproject.toml, Cargo.toml). YAML still dominates k8s and CI; JSON dominates wire payloads.

Tooling#

Tool

Notes

taplo

Standalone CLI formatter and linter. Speaks the spec precisely; integrates with editors.

tomli / tomllib (Python 3.11+)

Read-only parser in the stdlib.

tomli-w

Writer side; not in stdlib.

toml-rs

Reference Rust parser.

yj

Convert between YAML, JSON, TOML, HCL.

tomljson

Convert TOML to JSON from the command line.

$ taplo fmt pyproject.toml
$ taplo lint pyproject.toml
$ python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb')))"
$ yj -tj < config.toml > config.json

Pitfalls#

  • A table can only be defined once. Redefining [database] is an error; sub-tables under a previous table are fine.

  • Inline tables are immutable after their declaration — you cannot add keys to an inline table elsewhere in the file.

  • Dotted keys and bracketed headers interact subtly. Stick to one form per file.

  • TOML strings are UTF-8; ensure your editor doesn’t write a BOM (some parsers reject it).

References#