The basics
What is a TOML file?
A TOML file stores configuration as text: key = value lines, grouped into tables by [headers], much like an INI file. The difference is that TOML has a written specification and real types. A value in quotes is a string, 8080 is an integer, true is a boolean and 2026-10-27 is a date, and a parser in any language must read them the same way.
Tom Preston-Werner, a co-founder of GitHub, created it in 2013 as a config format that is easy to read and maps unambiguously to a hash table, the dictionary or object type every language has. You meet it mostly as a project's own settings file:
| File | Used by |
|---|---|
Cargo.toml, Cargo.lock | Rust: the package manifest and its lock file |
pyproject.toml | Python packaging (PEP 518, 2016) and tool settings for Ruff, pytest, Black and others |
uv.lock | The uv Python package manager's lock file |
hugo.toml | The Hugo static site generator |
netlify.toml | Build and redirect settings for Netlify |
Project.toml, Manifest.toml | Julia packages and environments |
libs.versions.toml | Gradle version catalogues |
In each case the file name is fixed by the tool, so a file called cargo.toml (lower case) or pyproject.TOML is simply not found on case-sensitive systems.
Anatomy
What's inside a TOML file
A TOML file is a list of key = value pairs. [table] headers group the pairs that follow them, and every value has an explicit type you can tell from how it's written. Here is a small but complete file, part by part.
# Settings for a small web app
title = "Sample app"
version = "2.10"
debug = false
port = 8080
ratio = 0.75
released = 2026-10-27T09:30:00Z
languages = ["en", "nl"]
owner = { name = "Ada Example", email = "ada@example.com" }
[database]
host = "db.example.internal"
pool.max = 10
[database.replica]
host = "replica.example.internal"
[[servers]]
name = "web-1"
[[servers]]
name = "web-2"- 1
# Settings for a small web appA comment
Everything from
#to the end of the line is ignored, unless the#is inside a string. - 2
title = "Sample app"A key and a string
Keys go on the left of
=, values on the right. Strings always need quotes: unlike YAML or INI, a bare word is an error, not text. - 3
version = "2.10"A version kept as text
Because strings are always quoted,
"2.10"can never turn into the number 2.1, the classic YAML trap. - 4
debug = falseA boolean
Only lowercase
trueandfalse.yes,onandTrueare errors. - 5
port = 8080 ratio = 0.75An integer and a float
Integers are 64-bit, may use
_as a separator (1_000_000) and may be written in hex, octal or binary (0xFF,0o755,0b1010). Floats need a digit on both sides of the point, andinfandnanare allowed. - 6
released = 2026-10-27T09:30:00ZA date and time
A native type, written as in RFC 3339. This one is a moment in UTC; TOML also has local date-times without a time zone, plain dates and plain times.
- 7
languages = ["en", "nl"]An array
Values in square brackets, separated by commas. Arrays may span several lines and end with a trailing comma.
- 8
owner = { name = …, email = … }An inline table
A small table written on one line. In TOML 1.0 it must stay on one line, with no trailing comma; TOML 1.1 relaxes both rules.
- 9
[database]A table header
Starts a table: every key below it, up to the next header, belongs to
database. Indenting the keys is allowed but means nothing. - 10
pool.max = 10A dotted key
Creates nested tables on the fly: this is
database.pool.max. Dotted keys and headers are two ways of writing the same structure. - 11
[database.replica]A sub-table
A dotted header creates a table inside
database. The parent doesn't need a header of its own. - 12
[[servers]]An array of tables
Double brackets add a new table to the
serverslist each time they appear, the TOML way to write a list of records.
How it works
How TOML turns into data
A parser turns a TOML file into nested tables (dictionaries). The anatomy example above loads as this, shown as JSON, with Python's tomllib:
{
"title": "Sample app",
"version": "2.10",
"debug": false,
"port": 8080,
"ratio": 0.75,
"released": "2026-10-27 09:30:00+00:00",
"languages": ["en", "nl"],
"owner": { "name": "Ada Example", "email": "ada@example.com" },
"database": {
"host": "db.example.internal",
"pool": { "max": 10 },
"replica": { "host": "replica.example.internal" }
},
"servers": [{ "name": "web-1" }, { "name": "web-2" }]
}- Headers and dotted keys build the same tree.
[database]followed bypool.max = 10gives the same result as[database.pool]followed bymax = 10. - Each key and each table can be defined once. Writing
porttwice in one table, or[database]twice, makes the whole file invalid. Arrays of tables ([[servers]]) are the exception: repeating them is the point. - Order of tables doesn't matter, but keys after a header always belong to that header. A top-level key placed below a table header ends up inside that table, a common mistake when adding settings to the end of a file.
- Dates come back as date objects in languages that have them, such as Python's
datetime. JSON has no date type, which is why the date above became a string.
| Written as | Name | Escapes | Use for |
|---|---|---|---|
"C:\\Users" | Basic | Yes: \n, \t, \", \u00E9 | Most text |
'C:\Users' | Literal | None: what you see is what you get | Windows paths, regular expressions |
"""…""" | Multi-line basic | Yes | Long text with escapes |
'''…''' | Multi-line literal | None | Long text, code, templates |
Versions
TOML 1.0 and TOML 1.1
TOML went through several 0.x versions before 1.0.0, released on 11 January 2021, froze the core. 1.1.0, released on 18 December 2025, is a small step: every valid TOML 1.0 file is still valid, and it accepts a few things 1.0 rejects:
- Inline tables may span several lines and end with a trailing comma.
- Two new escapes in basic strings:
\efor the escape character and\xHHfor a code point up to 255. - Seconds are optional in times and date-times:
03:15and2026-11-01T02:00. - Clarifications, including that parsers may support integers and floats beyond 64 bits.
# TOML 1.0: an inline table must fit on one line
owner = { name = "Ada Example", email = "ada@example.com" }
# TOML 1.1: it may span lines and end with a comma
owner = {
name = "Ada Example",
email = "ada@example.com",
}Support is arriving gradually. Python's tomllib reads TOML 1.1 from Python 3.15 (released October 2026); on Python 3.11 to 3.14 it reads 1.0 only and rejects the multi-line inline table above. Of the parsers we tested, tomli 2.5.0, tomlkit 0.15.2 and smol-toml 1.9.1 for JavaScript read sample-toml-1.1.toml; the tomllib in Python 3.13 stops at its first line of content.
Which version should you write?
Stick to TOML 1.0 syntax in files other people's tools will read, such as pyproject.toml or Cargo.toml, until you know every tool involved supports 1.1. A 1.0 file works everywhere.
Comparison
TOML vs YAML, JSON and INI
| TOML | [YAML](/file-types/yaml) | [JSON](/file-types/json) | [INI](/file-types/ini) | |
|---|---|---|---|---|
| Structure from | [tables] and key = value | Indentation | Brackets and braces | [sections] and key = value |
| Specification | Yes: TOML 1.1.0 | Yes: YAML 1.2.2 | Yes: RFC 8259 | None; every parser has its own dialect |
| Comments | Yes, # | Yes, # | No | Yes, ; and often # |
| Types | Explicit: quotes for strings, few and strict | Guessed from the text, version-dependent | Few and strict | Everything is text |
| Dates | Native date and time types | Timestamps in many parsers | None; strings by convention | None |
| Deep nesting | Possible, but long [a.b.c] headers get awkward | Natural | Natural | Usually two levels: section and key |
| Typical use | App and package config: pyproject.toml, Cargo.toml | Config people edit: CI, Kubernetes, Compose | APIs, data exchange, generated config | Older and Windows software, php.ini, Git-style config |
TOML's strictness is the point: there is no Norway problem, because NO without quotes is an error, not false. The price is that it suits flat or moderately nested settings better than deep trees. For large nested documents, such as Kubernetes manifests, YAML or JSON is the usual choice.
Troubleshooting
Why won't my TOML file load?
TOML parsers stop at the first problem and report the line and column. The messages below are from Python's tomllib; other parsers word them differently but catch the same mistakes.
| Written as | Error | Fix |
|---|---|---|
name = hello | Invalid value | Quote strings: name = "hello" |
debug = True or debug = yes | Invalid value | Booleans are lowercase true or false only |
port = 1 twice in a table | Cannot overwrite a value | Remove the duplicate key |
[database] twice | Cannot declare ('database',) twice | Merge the two tables, or use [[database]] if you meant a list |
path = "C:\Users\ada" | Invalid hex value | \U starts an escape. Use a literal string: 'C:\Users\ada' |
id = 01 | Expected newline or end of document after a statement | No leading zeros in integers; quote IDs and postcodes |
| A file saved with a UTF-8 BOM | Invalid statement (at line 1, column 1) | Save as UTF-8 without BOM. smol-toml accepts the BOM; tomllib and tomli don't |
| A multi-line inline table | Invalid initial character for a key part | The parser supports TOML 1.0 only: put the table on one line, or use a [table] header |
If the file loads but the settings have no effect, look at where they sit. A key added at the bottom of the file belongs to the last table header above it, not to the top level.
How to
How to open, edit and convert a TOML file
- Open and edit it: any text editor works on Windows, macOS, Linux or a phone. In VS Code, an extension based on Taplo, such as Even Better TOML, adds highlighting, formatting and validation, including schemas for
pyproject.tomlandCargo.toml. - Let the tool edit it:
cargo add serdeupdatesCargo.toml, anduv add httpxorpoetry add httpxupdatespyproject.toml, keeping the formatting and comments. - Read it from code: Python has
tomllibin the standard library since 3.11. It reads only; write with tomli-w, or edit while keeping comments with tomlkit. In Rust, thetomlcrate works with serde; in JavaScript, smol-toml reads and writes. - Convert to JSON: load it and write JSON, as below. Comments are lost, and dates need converting to strings.
import json, tomllib
with open("config.toml", "rb") as f: # tomllib wants binary mode
data = tomllib.load(f)
print(json.dumps(data, indent=2, default=str)) # dates become stringsSecurity
Is a TOML file safe? Secrets and build files
Parsing TOML is safe: unlike YAML, it has no tags that ask the parser to build objects, so loading a file can't run code. The risks are in what the files contain and what tools do with them:
- Secrets in config files. API keys and passwords in a committed
config.tomlornetlify.toml, or in a shared link, are a common leak. Keep them in environment variables or a secrets manager. Cargo stores your registry token separately, incredentials.tomlin the Cargo home folder, so thatCargo.tomlcan be shared safely. - Build files name code to run. Installing a Python package from source makes pip run the build backend named in its
pyproject.toml, and Cargo runs a crate'sbuild.rs. Reading those TOML files is harmless; building the project means trusting it. - Size limits. Parsers load the whole file into memory. Services that accept TOML uploads should cap their size.
Free downloads
Sample TOML files to download
Free sample TOML files for testing parsers, config loaders, editors and upload limits. All except sample-toml-1.1.toml are valid TOML 1.0 and load in Python's tomllib, tomli, tomlkit and smol-toml.
Loading…What's inside sample-config.toml
The file is commented: each numbered comment marks one of these parts.
Comments 1 to 4: root keys and simple values
Strings, a quoted version number, a boolean, integers with underscores and in octal, a float and
inf.Comment 5: dates and times
All four kinds: an offset date-time, a local date-time, a local date and a local time.
Comments 6 and 7: strings
A basic string with
\",\tand\u00e9escapes, literal strings for a Windows path and a regular expression, and a multi-line string.Comments 8 and 9: arrays and an inline table
A one-line array, a multi-line array with a trailing comma, and an
ownerinline table.Comments 10 to 12: tables
A
[database]table, dotted keys that createdatabase.pool, and a[database.replica]sub-table.Comment 13: quoted keys
Keys with a space, an accent and a dot, which need quotes.
Comment 14: an array of tables
Two
[[servers]]entries that load as a list of two tables.
All sample TOML files
Commented config file
sample-config.toml · 2.3 KB (2,359 bytes)
An app configuration with 14 numbered comments covering every value type, all four date and time types, the four kinds of strings, tables, dotted keys, quoted keys and an array of tables.
- Teaching the parts of a TOML file
- Testing config loaders and type handling
pyproject.toml for a fictional package
sample-pyproject.toml · 1.3 KB (1,355 bytes)
Packaging metadata in the standard
[build-system]and[project]tables, with dependencies, optional dependencies, a script entry point and[tool.*]settings for Ruff and pytest.- Testing tools that read `pyproject.toml`
- A starting point for a new Python package
Cargo.toml for a fictional crate
sample-cargo.toml · 1.0 KB (1,056 bytes)
A Rust manifest with dependencies as strings and inline tables, dev-dependencies, features, a
[[bin]]array of tables and a release profile. Rename it toCargo.tomlto use it.- Testing tools that read Cargo manifests
- Seeing inline tables and arrays of tables in real use
TOML 1.1 features (rejected by 1.0 parsers)
sample-toml-1.1.toml · 859 bytes
A multi-line inline table with a trailing comma, the new
\eand\xHHescapes and times without seconds. Valid TOML 1.1.0; a parser that supports only 1.0 should report an error.- Finding out which TOML version a parser supports
- Testing error reporting
Non-ASCII text with CRLF line endings
sample-unicode-crlf.toml · 794 bytes
Greetings in eight scripts and emoji, the same characters as
\uand\Uescapes, quoted keys such as"€", and a multi-line string, saved with Windows line endings.- Spotting encoding bugs
- Checking CRLF handling in multi-line strings
100 KB TOML file
sample-100kb.toml · 100 KB (102,400 bytes)
714 fictional sensors as an array of tables, padded with a final comment to exactly 102,400 bytes.
- Upload and size-limit tests
- Parser performance
Ways to test with them
- Which version your parser supports: load
sample-toml-1.1.toml. An error at theownertable means TOML 1.0 only. - Types: load
sample-config.tomland check thatversionis a string,permissionsis 493,timeoutis infinity andlaunch_dayis a date. - Line endings: in
sample-unicode-crlf.toml,notes.textshould come back as two lines; check whether your parser keeps the CR. - Upload limits:
sample-100kb.tomlis exactly 102,400 bytes, for testing a limit at the boundary.
Free to use for testing, teaching and demos, commercially or not, with no attribution needed. A link back to this page is always appreciated.
Put it online
Have a TOML file? Give it a link.
Drop a TOML file on 99helpers and it gets its own address, for sharing a config example or a test fixture. Opened in a browser, the link shows the file as plain text, and scripts can fetch it from the same link.
Questions
TOML files, frequently asked
What does TOML stand for?
Tom's Obvious Minimal Language, after its creator, Tom Preston-Werner.
How do I open a TOML file?
With any text editor, such as Notepad, TextEdit in plain-text mode or VS Code. A TOML extension in your editor adds highlighting and error checking.
Is TOML better than YAML?
For hand-edited settings, TOML is harder to get wrong: strings need quotes and nothing is guessed. YAML is better at deeply nested data and is the norm for CI and Kubernetes. Use what the tool expects.
What is the difference between TOML and INI?
They look alike, but INI has no specification and treats every value as text, so parsers disagree. TOML has a spec, types and nesting, so every compliant parser reads a file the same way.
Which TOML version is current?
TOML 1.1.0, released in December 2025. Many parsers still support only 1.0.0 (2021), so files meant for others should stick to 1.0 syntax.
How do I read a TOML file in Python?
Use tomllib from the standard library (Python 3.11 and later): open the file in binary mode and call tomllib.load(f). To write TOML, use tomli-w or tomlkit.
Can a TOML file have comments?
Yes. Everything after # on a line is a comment, unless the # is inside a string.
How do I convert TOML to JSON?
Load it with a TOML library and write it out with a JSON library. Comments are lost, and dates must be turned into strings because JSON has no date type.
Related formats
Other file types explained
Keep going
Related
Sources
- TOML v1.1.0 specification
- TOML v1.0.0 specification
- Python documentation: tomllib
- IANA media types registry (application/toml)
- PEP 518: build system requirements (pyproject.toml)
- TOML (Wikipedia): history and adoption
Last reviewed 27 October 2026.