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