TOML Cheatsheet

Real Config Examples

Use this TOML reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

pyproject.toml

The canonical modern Python project file: build backend, PEP 621 metadata, and tool tables. Every block on this page was parsed with tomllib before publishing.

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

[project]
name = "hackuni-tools"
version = "0.3.1"
description = "Internal CLI helpers."
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
keywords = ["cli", "tooling"]
authors = [{ name = "Pablo Valdes", email = "pv@example.com" }]
classifiers = [
  "Programming Language :: Python :: 3.11",
  "Programming Language :: Python :: 3.12",
  "License :: OSI Approved :: MIT License",
]
dependencies = [
  "click>=8.1",
  "httpx>=0.27",
  "pydantic>=2.7",
]

[project.optional-dependencies]
dev = ["pytest>=8.2", "mypy>=1.10", "ruff>=0.5"]

[project.urls]
Homepage = "https://example.com"
Source = "https://github.com/example/hackuni-tools"

[project.scripts]
hu = "hackuni_tools.cli:main"

[tool.hatch.build.targets.wheel]
packages = ["src/hackuni_tools"]

license = { text = "MIT" } is an inline table, and authors is an array of inline tables. Both are single, final values, which is exactly what inline tables are for.

[tool.ruff] and [tool.mypy]

Tool tables live under [tool.<name>] by convention, and the tool owns everything below its own key.

[tool.ruff]
line-length = 100
target-version = "py311"
extend-exclude = ["migrations", "build"]

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"]
fixable = ["ALL"]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
"tests/*" = ["S101"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

[tool.mypy]
python_version = "3.11"
strict = true
warn_unreachable = true

[[tool.mypy.overrides]]
module = ["yaml.*", "tomlkit.*"]
ignore_missing_imports = true

[tool.pytest.ini_options]
addopts = "-ra --strict-markers"
testpaths = ["tests"]

Note "__init__.py" = ["F401"] uses a quoted key, since a bare key cannot contain a dot. Do not write tool.ruff.lint.select = ["E"] as a dotted key anywhere in this file, because that makes the later [tool.ruff.lint] header a redefinition and the whole file fails to parse.

Cargo.toml

[package]
name = "hu-indexer"
version = "0.2.0"
edition = "2021"
rust-version = "1.78"
authors = ["Pablo Valdes <pv@example.com>"]
license = "MIT OR Apache-2.0"
description = "Builds the search index."
repository = "https://github.com/example/hu-indexer"
default-run = "hu-indexer"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.38", features = ["full"] }
anyhow = "1.0"
clap = { version = "4.5", features = ["derive"], default-features = false }

[dev-dependencies]
criterion = "0.5"

[features]
default = ["cli"]
cli = []
tracing = ["dep:tracing"]

[[bin]]
name = "hu-indexer"
path = "src/main.rs"

[[bin]]
name = "hu-dump"
path = "src/bin/dump.rs"

[[bench]]
name = "index"
harness = false

[profile.release]
lto = true
codegen-units = 1
strip = "symbols"

Two patterns to copy. A dependency is either a bare version string (anyhow = "1.0") or an inline table when it needs options. Multiple binaries are [[bin]] blocks, one per executable, which is array-of-tables doing exactly the job it was designed for.

fly.toml

A deploy config, and the densest real use of [[...]] you will meet.

app = "hackuni-api"
primary_region = "ewr"

[build]
dockerfile = "Dockerfile"

[env]
PORT = "8080"
LOG_LEVEL = "info"

[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 1

[http_service.concurrency]
type = "requests"
soft_limit = 200
hard_limit = 250

[[http_service.checks]]
grace_period = "10s"
interval = "30s"
method = "GET"
path = "/healthz"
timeout = "5s"

[[mounts]]
source = "data"
destination = "/var/lib/app"

[[vm]]
size = "shared-cpu-1x"
memory = "512mb"
cpus = 1

PORT = "8080" is a string on purpose, because environment variables are always strings and Fly rejects an integer there. This is the most common mistake in a [env] block, in any TOML deploy config.

netlify.toml

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

[build.environment]
NODE_VERSION = "22"

[context.production.environment]
NEXT_PUBLIC_ENV = "production"

[[redirects]]
from = "/old-docs/*"
to = "/docs/:splat"
status = 301
force = true

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

[[headers]]
for = "/assets/*"
  [headers.values]
  Cache-Control = "public, max-age=31536000, immutable"

[[plugins]]
package = "@netlify/plugin-lighthouse"

The indented [headers.values] under [[headers]] is Netlify's own documented style. Indentation is not significant in TOML, so that header binds to the most recent [[headers]] element regardless of how it is indented. Do not read the indentation as scoping.

Tool tables: uv and rustfmt

[tool.uv]
package = true
default-groups = ["dev"]

[tool.uv.sources]
hackuni-core = { path = "../core", editable = true }

[dependency-groups]
dev = ["pytest>=8.2", "ruff>=0.5"]
# rustfmt.toml, a standalone file with no [table] header at all
edition = "2021"
max_width = 100
use_field_init_shorthand = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
newline_style = "Unix"

rustfmt.toml is a whole valid TOML file with no headers, which is the point of the format: a flat list of settings needs no ceremony. Validate any of these with:

python -c "import tomllib;tomllib.load(open('pyproject.toml','rb'));print('ok')"
taplo lint pyproject.toml