T tomlkit·org
Inspect Formatter Validator Convert TOMLJSON JSONTOML TOMLYAML YAMLTOML INITOML TOMLINI .envTOML TOML.env TOMLTS Transform Sort keys Flatten Minify Compare Diff Merge

INI to TOML Converter

updated 8 June 2026

Migrate a legacy INI file to TOML. Sections become tables, dotted section names become nested tables, and obvious types (numbers, booleans) are coerced from the strings.

dotted [a.b] sections become nested tables

What this tool does

It reads a legacy INI file line by line, builds an object out of the sections and key/value pairs it finds, and emits the result as canonical TOML 1.0. An [section] header opens a table, a dotted [a.b.c] header opens a nested one, and each key = value (or key: value) line becomes a key in the current table. With type inference on, bare values that look like numbers or booleans are coerced; everything else stays a string.

The intent it closes: "I have an old .ini / .conf config and I'm about to rewrite the loader, so I want it in TOML." The usual destinations are a pyproject.toml, a Cargo.toml, a Hugo or Zola config.toml, or any Python, Go, or Rust service that's moving off its old INI loader.

One thing INI can't give the converter up front: INI has no array syntax. There is no way to know that tags = a, b, c was meant as a list, so every key comes out as a single scalar. If you want a TOML array, you wrap it by hand after the conversion.

When you'd reach for it

  • Modernize a hand-written app config. An old Python, PHP, or .NET app ships an .ini and you're moving its loader to TOML — paste the file and convert in one pass.
  • Get real types out of stringly INI. Leave Infer types on and port = 5432 becomes the integer 5432, debug = false becomes the boolean false — no manual retyping.
  • Keep version strings as strings. Switch Infer types to Keep as strings when a value like version = 1.0 must stay text and not collapse to a float.
  • Honour a non-standard comment char. If your dialect only treats ; as a comment (and a literal # is real data), pick the ; only option so the # survives.
  • Flatten dotted sections into nested tables. A [database.pool] header lands as a proper nested [database.pool] table under [database].
  • Alphabetize while you convert. Pick Alphabetical sort to land a tidy, diff-stable file instead of whatever order the INI happened to use.
  • Convert a config full of secrets safely. Parsing and emission run in your tab, so an .ini holding a password or token never leaves the machine.

How the conversion works

One click of Convert runs three stages.

1. Strip comments, line by line

Each line is scanned left to right. When a quote (" or ') opens, the scanner skips to its matching close — honouring backslash escapes — so a comment character inside a quoted value is left alone. Outside quotes, the first character that appears in your Comments set truncates the rest of the line. A line that is empty after trimming is dropped.

2. Build the section tree

A line matching [name] opens a section; the name is split on . so [a.b.c] walks down into nested objects. Every other line is matched against key = value or key: value — the first = or : wins, and both sides are trimmed. Keys land in whichever section is currently open; before the first header, they land at the root. If the same key appears twice in a section, the last write wins.

3. Type each value, then emit TOML

A value wrapped in matching quotes is unquoted, with backslash escapes resolved (\" becomes "). An unquoted value is left as a plain string unless Infer types is on, in which case true/false/yes/no/on/off become booleans, a run of digits becomes an integer, and a digits-dot-digits value becomes a float. The finished tree is handed to the same canonical writer the formatter uses, so it respects the Sort option and writes a blank line between sections. If nothing parsed — no sections and no keys — the status bar shows No INI sections or keys found. and no output is written.

Options reference

Comments

Which characters start a comment outside a quoted value. ; and # (the default) treats both as comment markers, matching the most permissive INI dialects. ; only treats # as ordinary data — pick it when a value legitimately contains a #, like a colour #ff8800 or a URL fragment. # only does the reverse for files that use # exclusively. The choice only affects unquoted text; a comment character inside "…" or '…' is always kept.

Infer types

Yes (numbers/bools) coerces bare values: true, false, yes, no, on, and off (any case) become real booleans, a whole-number literal becomes a TOML integer, and a decimal literal becomes a float. Anything that doesn't match those shapes — including dates, IPs, and version strings — stays a quoted string. Keep as strings turns inference off entirely, so every unquoted value is emitted as a string; reach for it when a value like 1.0 or 007 must not be reinterpreted as a number.

Sort

Preserve writes keys and sections in the order they appeared in the INI. Alphabetical sorts every table's keys A–Z, which gives you a diff-stable file that doesn't churn when you re-run the conversion. There is no inline-table control on this widget, so every section is written as a [header] block rather than an inline { … } table.

Output and mapping rules

  • [section] → TOML table. Header names map straight across.
  • [a.b] → nested table. Dotted headers walk into nested objects and re-emit as [a.b].
  • key = value and key: value are both accepted; the first = or : separates key from value.
  • Comments (lines or trailing text starting with a char from your Comments set, outside quotes) are stripped and never reach the output.
  • Quoted strings ("…" or '…') are unquoted, with backslash escapes resolved, before being re-emitted as TOML strings.
  • Booleans and numbers are inferred only with Infer types on; otherwise every unquoted value stays a string.
  • No arrays. INI has no list syntax, so each key is a single scalar — tags = a, b, c becomes the string "a, b, c".
  • Encoding and line endings: UTF-8, LF, with a blank line between sections.
  • Download: output.toml, content type text/plain;charset=utf-8.

Example

Input:

; legacy app config
app_name = MyApp
debug = false

[database]
host = localhost
port = 5432
password = "s3cret!"

[database.pool]
size = 10
timeout = 30

Output (defaults — Comments ; and #, Infer types on, Preserve order):

app_name = "MyApp"
debug = false

[database]
host = "localhost"
port = 5432
password = "s3cret!"

[database.pool]
size = 10
timeout = 30

The leading ; comment is gone. debug became a real boolean and port a real integer, while localhost stayed a string because it matches no numeric or boolean shape. The quoted password kept its exclamation mark verbatim, and the dotted [database.pool] header landed as a nested table.

Recipes by intent

Migrate an old app config to TOML

Leave every option on its default — Comments ; and #, Infer types on, Preserve order. Paste the INI, click Convert, and you get a typed TOML file in the same layout your INI already had. Run the result through the validator before wiring up the new loader.

Keep numeric-looking values as text

Switch Infer types to Keep as strings. A version = 1.0 stays the string "1.0" instead of collapsing to the float 1, and a zero-padded id like 007 keeps its zeros. Quote individual values in the source if you want a mix.

Convert a file that uses # as data

Set Comments to ; only. A value such as color = #ff8800 survives intact because # is no longer treated as the start of a comment — only ; truncates a line.

Produce a diff-stable config file

Set Sort to Alphabetical. The same INI always yields the same byte-ordered TOML, so re-running the conversion never reshuffles keys in version control.

Limits and performance

  • In-memory. The INI text, the parsed object, and the TOML output all sit in memory at once. Config-sized files convert instantly; a multi-megabyte INI can take a moment to scan and emit. As a rule of thumb these tools are comfortable to a few hundred MB on desktop and roughly 100 MB on mobile.
  • The textarea is the bottleneck on huge output. Past tens of megabytes, painting the result back into the right pane lags after the conversion itself finishes — use Download .toml rather than Copy.
  • Line-oriented, not streaming. The whole file is split on newlines before parsing, so there's no incremental mode; for genuinely large legacy dumps, convert in chunks by section.

Errors and how to fix them

No INI sections or keys found.

The parser ran but produced an empty object — usually because the input is blank, is entirely comments, or has no line that matches key = value / key: value. Check that your separators are = or : and that the values aren't all being eaten as comments by your Comments setting.

A value got truncated at a # or ;

That character is in your Comments set and appeared outside quotes, so everything after it was stripped. Either wrap the value in quotes (url = "https://x/#frag") or narrow the Comments option to the single character your file actually uses for comments.

A version or id turned into a number

Infer types is on, so 1.0 became the float 1 and 007 lost its leading zeros. Switch Infer types to Keep as strings, or quote that one value in the source so it's treated as text.

A key appears once when my INI had it twice

Expected. Within a section the last assignment to a key wins — INI has no spec for repeated keys, so this matches what most parsers do. Rename or remove the duplicate in the source if you need both.

My comma-separated list came out as a string

INI has no array syntax, so tags = a, b, c is read as one scalar and emitted as "a, b, c". Edit the output by hand to wrap it in [ … ] if you want a TOML array.

FAQ

What if my INI has duplicate keys?

The last write wins within a section. INI has no spec to lean on here, so this matches the behaviour of most real-world parsers. Consolidate the duplicates in your source if both values matter.

How do I get a TOML array out of a comma-separated INI value?

You can't automatically — INI doesn't mark a value as a list, so a, b, c stays a single string. Convert first, then wrap the value in [ … ] by hand in the output.

Is the output canonical TOML 1.0?

Yes. The converter parses the INI into a plain object, then hands it to the same canonical TOML writer the formatter uses — consistent spacing around =, deterministic table layout, and a blank line between sections.

Will it keep my comments?

No. Comments are stripped during parsing and never reach the value tree, so the emitter has nothing to write. Keep your original .ini as the source of truth if the comments matter.

Is my data uploaded?

Never. Parsing and emission run entirely in your browser. You can go offline after the page loads and the converter still works — safe for an .ini holding passwords or API keys.