TOML Configuration: Syntax Guide, Validation & Best Practices [2026]

TOML has quietly become one of the most widely used configuration file formats in the software industry without receiving the fanfare of YAML or JSON. If you have written a Rust project, you have written TOML — every Cargo.toml file is TOML. If you have configured a modern Python package, you have likely encountered pyproject.toml. If you have deployed to Netlify, your netlify.toml is TOML.

Despite this ubiquity, TOML is less documented and less discussed than its competitors. This guide explains the language from scratch: what TOML is, how its syntax works, how it compares to YAML and JSON, and how to validate and work with TOML effectively in real projects.

1. What Is TOML?

TOML stands for Tom's Obvious, Minimal Language. It was created by Tom Preston-Werner (co-founder of GitHub) in 2013 as a configuration file format with clearer semantics than YAML and better readability than JSON. The specification reached version 1.0.0 in 2021, providing a stable, unambiguous foundation that has driven its adoption.

TOML's core design goals are:

TOML files use the .toml extension and are encoded in UTF-8. Comments use the # character (same as YAML and Python).

2. TOML vs YAML vs JSON

Feature TOML YAML JSON
Comments Yes (#) Yes (#) No
Readability High High Moderate
Implicit type coercion None Yes (Norway problem) None
Date/time type Native (RFC 3339) Native (often problematic) No (string only)
Whitespace significance No (except newlines) Yes (indentation defines structure) No
Anchors / reuse No Yes No
Deep nesting Awkward Natural Natural (but verbose)
Spec stability v1.0 (2021), stable YAML 1.2 (2009), many parsers use 1.1 RFC 8259, very stable
API use Rare Rare Universal
Config file use Rust, Python, Netlify, Hugo Kubernetes, Docker, GitHub Actions Node.js, TypeScript, VS Code

TOML occupies a specific niche: it is better than JSON for configuration (it has comments and native datetime) and safer than YAML (no implicit type coercion). Its limitation is that it does not handle deeply nested or complex structures as elegantly — which is why Kubernetes uses YAML rather than TOML.

3. TOML Syntax Basics

Key-Value Pairs

The fundamental unit of TOML is the key-value pair: a key, an equals sign, and a value on a single line. Keys can be bare (unquoted alphanumeric), quoted with double quotes, or quoted with single quotes:

# Bare key
name = "Alice"
age = 30
active = true

# Quoted key (required for keys with spaces or special chars)
"full name" = "Alice Smith"
"app.version" = "1.0.0"

# Dotted key — creates nested structure inline
server.host = "localhost"
server.port = 8080

Strings

TOML has four string types:

# Basic string — supports escape sequences (\n, \t, \", \\)
message = "Hello, World!\nSecond line"

# Literal string — no escape processing (use for regex, Windows paths)
path = 'C:\Users\Alice\Documents'

# Multi-line basic string — preserves newlines, supports escapes
description = """
This is a
multi-line string.
"""

# Multi-line literal string — no escapes, preserves everything
raw = '''
All \backslashes\ are literal here.
'''

Numbers

integer    = 42
negative   = -17
hex_value  = 0xDEADBEEF
octal      = 0o755
binary     = 0b11010110
float      = 3.14159
scientific = 5e+22
infinity   = inf
nan        = nan

4. Tables and Arrays of Tables

Tables are TOML's mechanism for grouping related key-value pairs. They are equivalent to nested objects in JSON or mappings in YAML.

Standard Tables

[database]
host = "localhost"
port = 5432
name = "myapp"

[server]
host = "0.0.0.0"
port = 8080
debug = false

This is equivalent to the JSON:

{
  "database": { "host": "localhost", "port": 5432, "name": "myapp" },
  "server": { "host": "0.0.0.0", "port": 8080, "debug": false }
}

Nested Tables

Dotted notation creates nested tables without requiring a separate header for each level:

[database.primary]
host = "db.prod.example.com"
port = 5432

[database.replica]
host = "db.replica.example.com"
port = 5432

Arrays of Tables

Double brackets [[table]] define an array of tables — equivalent to a JSON array of objects. This is one of TOML's most important and most confusing features:

[[server]]
name = "web-1"
ip = "10.0.0.1"

[[server]]
name = "web-2"
ip = "10.0.0.2"

[[server]]
name = "web-3"
ip = "10.0.0.3"

This creates an array server containing three objects. The equivalent JSON:

{
  "server": [
    {"name": "web-1", "ip": "10.0.0.1"},
    {"name": "web-2", "ip": "10.0.0.2"},
    {"name": "web-3", "ip": "10.0.0.3"}
  ]
}

Convert Between TOML, YAML, and JSON

Working across different config formats? The SnapUtils YAML to JSON converter handles YAML and JSON conversions instantly. Pair it with a TOML library to move between all three formats.

Open YAML to JSON Converter

5. TOML Data Types

TOML has a richer native type system than JSON, particularly for dates and times:

Type Example Notes
String "hello" Four variants (basic, literal, multi-line)
Integer 42, -17, 0xFF Supports hex, octal, binary notation
Float 3.14, 1e10, inf IEEE 754 double precision
Boolean true, false Lowercase only; no coercion from yes/no/on/off
Offset DateTime 2026-04-19T09:00:00Z RFC 3339 with timezone offset
Local DateTime 2026-04-19T09:00:00 No timezone (local time)
Local Date 2026-04-19 Date only, no time
Local Time 09:00:00 Time only, no date
Array [1, 2, 3] All elements must be same type (or mixed in TOML 1.0)
Inline Table {x = 1, y = 2} Single-line table; must be complete, no modification allowed

The native datetime types are a significant advantage over both YAML (where datetime parsing varies by parser) and JSON (which has no datetime type at all). In TOML, created_at = 2026-04-19T09:00:00Z is unambiguously a datetime, not a string that some parser might or might not convert.

6. Real-World TOML Examples

Cargo.toml (Rust)

[package]
name = "my-app"
version = "0.1.0"
edition = "2021"
description = "A sample Rust application"
authors = ["Alice Smith <alice@example.com>"]

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
anyhow = "1.0"

[dev-dependencies]
mockall = "0.11"

[profile.release]
opt-level = 3
lto = true
codegen-units = 1

pyproject.toml (Python)

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "my-python-app"
version = "0.1.0"
description = "A Python application"
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.100.0",
    "pydantic>=2.0",
    "sqlalchemy>=2.0",
]

[project.optional-dependencies]
dev = [
    "pytest",
    "ruff",
    "mypy",
]

[tool.ruff]
line-length = 100
select = ["E", "F", "W"]

netlify.toml

[build]
  command = "npm run build"
  publish = "dist"

[build.environment]
  NODE_VERSION = "20"

[[redirects]]
  from = "/api/*"
  to = "https://api.example.com/:splat"
  status = 200
  force = true

[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-Content-Type-Options = "nosniff"

7. TOML Validation in CI/CD

Validating TOML files in CI prevents broken configuration from reaching production. Unlike YAML (where a syntactically valid file can still produce surprising behavior), a valid TOML file always parses to the expected structure — validation primarily catches syntax errors.

Using taplo (Recommended CLI)

Taplo is the most comprehensive TOML toolchain, providing validation, formatting, and schema enforcement:

# Install taplo
cargo install taplo-cli

# Validate a single file
taplo check Cargo.toml

# Validate all TOML files in a directory
taplo check '**/*.toml'

# Format TOML files (auto-fix whitespace, sorting)
taplo fmt Cargo.toml

GitHub Actions Example

name: Validate TOML
on: [push, pull_request]

jobs:
  validate-toml:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install taplo
        run: cargo install taplo-cli --locked
      - name: Validate TOML files
        run: taplo check '**/*.toml'

Python (Built-in, Python 3.11+)

import tomllib
import sys

def validate_toml(path):
    try:
        with open(path, "rb") as f:
            tomllib.load(f)
        print(f"Valid: {path}")
    except tomllib.TOMLDecodeError as e:
        print(f"Invalid: {path}: {e}")
        sys.exit(1)

validate_toml("pyproject.toml")

8. Common TOML Pitfalls

Redefining a Key

TOML is strict: you cannot define the same key twice. This is a common mistake when migrating from INI files or when multiple team members edit the same file:

# INVALID: duplicate key
port = 8080
port = 9090  # Error: key 'port' already defined

Similarly, you cannot define a table header after a dotted key has already defined that namespace in the same scope.

Inline Tables Are Immutable

Once an inline table is defined, you cannot add keys to it with dotted notation elsewhere in the file:

# INVALID
server = {host = "localhost"}
server.port = 8080  # Error: cannot extend an inline table

Use a regular table header [server] instead of an inline table if you need to add keys in separate lines.

Array Element Type Consistency

In TOML 0.5 and earlier, all elements in an array had to be the same type. TOML 1.0 relaxed this restriction, but many parsers still enforce homogeneous arrays. Be explicit about types to ensure maximum parser compatibility:

# May fail in older parsers
values = [1, "two", 3.0]

# Safer — explicit string conversion
values = ["1", "two", "3.0"]

Super Tables Cannot Follow Their Sub-Tables

In TOML, a table header must appear before any of its sub-tables. The following is invalid:

# INVALID: [fruit] must come before [fruit.apple]
[fruit.apple]
color = "red"

[fruit]      # Error: [fruit] defined after [fruit.apple]
category = "produce"

Dotted Keys and Table Headers Conflict

A dotted key and a table header that resolve to the same path will conflict. Always be consistent within a file — use either dotted keys or table headers to define a namespace, not both:

# INVALID: both define the same 'server' namespace
server.host = "localhost"
[server]       # Error: conflicts with dotted key above
port = 8080

9. Frequently Asked Questions

What does TOML stand for?

TOML stands for Tom's Obvious, Minimal Language, created by Tom Preston-Werner (co-founder of GitHub) in 2013. The name signals its philosophy: it should be obviously readable to humans, minimal in complexity, and easy to parse into a hash table data structure. TOML reached version 1.0 in January 2021, which provided a stable specification that the ecosystem has built upon. Files use the .toml extension.

Is TOML better than YAML?

TOML excels at flat-to-moderately-nested configuration with clear key-value semantics. It has no implicit type coercion (no Norway problem), a stable unambiguous specification, and native datetime support. YAML excels at complex nested structures, large configuration hierarchies, and environments where anchors and aliases provide significant DRY benefits. TOML is the better choice for tool-specific configuration files (Cargo.toml, pyproject.toml). YAML is the better choice for orchestration and workflow definitions (Kubernetes, GitHub Actions, Ansible). The two formats fill different niches rather than directly competing.

What files use TOML?

The most widely encountered TOML files are Cargo.toml (Rust package and workspace configuration), pyproject.toml (Python project metadata per PEP 517/518/621), netlify.toml (Netlify deployment and redirect configuration), config.toml (Hugo static site generator), and .cargo/config.toml (Rust build and target configuration). Many newer tools choose TOML for their configuration format because of its stability and clarity — it is increasingly common in Rust-written CLI tools and DevOps utilities.

How do I validate a TOML file?

The most complete CLI tool is taplo: install it with cargo install taplo-cli and run taplo check yourfile.toml. In Python 3.11+, the standard library includes tomllib: run python -c "import tomllib; tomllib.load(open('yourfile.toml','rb'))" — any syntax error will raise a TOMLDecodeError. For CI/CD, integrate taplo or a language-native TOML parser into a validation step that runs on every push and pull request. Catching TOML errors in CI is far cheaper than discovering them after a failed deployment.

Work with YAML and JSON Alongside TOML

Need to convert YAML configuration to JSON for API consumption? The SnapUtils YAML to JSON converter handles the conversion instantly — paste your YAML and get valid JSON output in one click.

Open YAML to JSON Converter Free

Related guides: YAML vs JSON  •  HTML Entities Guide  •  URL Slug Guide