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

TOML to .env Converter

updated 8 June 2026

Flatten a TOML configuration into KEY=VALUE pairs suitable for a .env file, Docker Compose, Kubernetes ConfigMaps, or any 12-factor environment loader. Nested tables become double-underscore separators.

What this tool does

It parses a TOML 1.0 document into a value tree, walks that tree depth-first, and flattens every scalar into a single KEY=VALUE line. Nested tables collapse into one flat key: the path segments are joined with a separator (__ by default), each segment is upper-cased, and any character that isn't a letter, digit, or underscore is replaced with _. So database.host becomes DATABASE__HOST on one line, not a nested block.

The intent it closes: "I keep my config in TOML, but the thing I'm deploying to only reads environment variables." The destinations are operational — a .env file for direnv or dotenv, a Docker Compose environment: block, a Kubernetes ConfigMap, or a 12-factor app on Heroku, Fly, or Railway. Frameworks like Spring Boot and .NET already read DATABASE__HOST as database.host, so this mapping drops straight in.

One thing to know going in: comments don't survive. The parser builds a value tree with no # comment nodes, so the emitter has nothing to write — only keys and values come across.

When you'd reach for it

  • Hand a Docker Compose file its environment: values. Switch Syntax to docker-compose and you get KEY: value lines you can paste straight under a service.
  • Source a config into a shell. Pick shell (export KEY=val) so every line is export KEY=value and a single source out.env loads the whole config.
  • Feed a framework that reads double-underscore keys. Keep the default __ separator and UPPER case, and Spring Boot, .NET Configuration, and Helm read the flattened names back as nested config.
  • Namespace every variable. Type a Prefix like MYAPP and every key comes out as MYAPP__DATABASE__HOST, so two services' configs won't collide in one environment.
  • Decide what happens to arrays before they hit a flat format. .env has no array type, so choose JSON-encoded, Comma-joined, or Drop rather than getting a silent default you didn't expect.
  • Match a tool that wants lower-case names. Set Case to lower for loaders that key on lower-case variables.

How the conversion works

One click of Convert runs three stages.

1. Parse the TOML

The full TOML 1.0 grammar is parsed into a value tree — dotted keys collapse into nested tables, datetimes become date-shaped values, integers keep their numeric type. A parse failure shows in the status bar and produces no output, so a clean conversion is also a syntax-valid file.

2. Flatten the tree to single keys

The tree is walked depth-first. Each leaf scalar's full path (for example databasepoolsize) is turned into one key by upper-casing each segment, replacing any non-[A-Za-z0-9_] character with _, and joining the segments with the Separator. With the default __ that yields DATABASE__POOL__SIZE. If a Prefix is set, it's cleaned the same way and prepended with the same separator. Arrays-of-tables and other non-scalar leaves are not produced — only tables (which recurse) and scalars (which emit) and arrays (handled next) exist at the leaves.

3. Emit one line per scalar

Each leaf becomes a line in the chosen Syntax. String values that contain whitespace, a quote, $, #, or a backslash — or that are empty — are wrapped in double quotes, with \, ", $, and backtick escaped. Booleans emit as true/false, numbers as their canonical form, and datetimes as ISO 8601 strings. Arrays are handled per the Arrays policy. The output ends with a trailing newline when there's at least one line.

Options reference

Separator

The string used to join the path segments of a nested key. __ (default) is the convention Spring Boot, .NET, and Helm understand as a nesting marker. _ is flatter but ambiguous if your own keys already contain underscores. . matches how some tools name variables, but note that most shells refuse a . inside an environment variable name, so it's only safe for loaders that read the file themselves rather than exporting into a shell.

Case

UPPER (default) upper-cases every key segment, which is the dominant convention for environment variables. lower lower-cases them for loaders that key on lower-case names. preserve leaves each segment's case exactly as it appeared in the TOML — useful when your keys are deliberately mixed-case and a consumer is case-sensitive.

Syntax

.env (KEY=val) (default) writes plain KEY=value lines for a .env file. shell (export KEY=val) prepends export so the file can be sourced into a shell. docker-compose writes KEY: value, the YAML-style shape used under a Compose service's environment: key; in this mode a value is additionally quoted if it contains a space, a colon, or a # (so 0.0.0.0:8080 comes out quoted), which the other two syntaxes leave bare.

Arrays

A flat key/value format has no array type, so you choose how a TOML array is represented. JSON-encoded (default) writes the array as a JSON string in double quotes, e.g. TAGS="[\"prod\",\"api\"]" — lossless and parseable on the other side. Comma-joined writes the elements joined by a single comma with no spaces (prod,api), the form many env loaders split on; it's quoted only if an element contains a special character. Drop skips array-valued keys entirely, for when the target can't use them at all.

Prefix

An optional namespace prepended to every key. It's cleaned to [A-Za-z0-9_], re-cased per the Case option, and joined with the same separator — so a prefix of MYAPP turns DATABASE__HOST into MYAPP__DATABASE__HOST. Leave it blank for no prefix.

Output and mapping rules

  • Nested tables → one flat key. Path segments are joined with the Separator; database.pool.sizeDATABASE__POOL__SIZE.
  • Key sanitizing. Each segment is re-cased per Case, and any character outside [A-Za-z0-9_] becomes _ (so key-nameKEY_NAME).
  • String quoting. A string is wrapped in double quotes when it contains whitespace, ", ', $, \, #, or is empty; inside the quotes \, ", $, and backtick are backslash-escaped. Otherwise it's written bare.
  • Scalars. Booleans render as true/false; integers and floats in their canonical form; datetimes as ISO 8601 strings.
  • Arrays. Per the Arrays policy: a quoted JSON string, a comma-joined string, or dropped.
  • Comments are not carried through — parse-and-re-emit drops them.
  • Encoding: UTF-8. Download: .env, content type text/plain;charset=utf-8.

Example

Input (TOML):

app_name = "MyApp"
debug = false

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

[server]
listen = "0.0.0.0:8080"
workers = 4
tags = ["prod", "api"]

Output (defaults — __ separator, UPPER case, .env syntax, JSON-encoded arrays):

APP_NAME=MyApp
DEBUG=false
DATABASE__HOST=localhost
DATABASE__PORT=5432
DATABASE__PASSWORD=s3cret!
SERVER__LISTEN=0.0.0.0:8080
SERVER__WORKERS=4
SERVER__TAGS="[\"prod\",\"api\"]"

Note that the table hierarchy is gone — [database] is now a DATABASE__ prefix on flat keys, not a block. 0.0.0.0:8080 stays unquoted here because a colon isn't special in .env syntax; switch Syntax to docker-compose and that same value comes out as SERVER__LISTEN: "0.0.0.0:8080". The tags array is encoded as a quoted JSON string under the default policy.

Recipes by intent

Build a Docker Compose environment: block

Set Syntax to docker-compose. You get KEY: value lines, with values containing a space, colon, or # auto-quoted — paste them straight under a service's environment:. Keep Arrays on Comma-joined if your container splits multi-value vars on commas.

Make a sourceable shell file

Set Syntax to shell (export KEY=val). Every line becomes export KEY=value, so a single source config.env loads the whole config into the current shell. Strings with spaces or shell metacharacters come pre-quoted and escaped.

Target a Spring Boot or .NET app

Keep the defaults: __ separator and UPPER case. Both frameworks rebind DATABASE__HOST to database.host automatically, so the flat file maps back onto your nested config with no extra work.

Namespace two services in one environment

Give each its own Prefix (API, WORKER). Every key gains that prefix plus the separator, so API__DATABASE__HOST and WORKER__DATABASE__HOST coexist without clashing.

Drop arrays the target can't use

Set Arrays to Drop. Array-valued keys are skipped entirely instead of being squeezed into a string, which is cleaner when the consumer would choke on a JSON or comma blob.

Limits and performance

  • In-memory. Input, parsed tree, and the flattened output coexist in memory. A config-sized file (a few hundred KB) converts instantly; the practical ceiling is roughly 500 MB on desktop and 100 MB on mobile before the tab struggles.
  • The textarea is the slow part on big output. Tens of megabytes paint slowly into the right-hand pane even after the conversion finishes — prefer Download .env over Copy for large results.
  • Flattening is lossy by design. Table structure becomes a naming convention, and arrays become strings (or vanish). That's the trade for a flat format, not a bug.
  • Comments are not preserved. Keep your TOML as the source of truth and convert a copy.

Errors and how to fix them

Status bar goes red on click / a parse error

The input isn't valid TOML 1.0 — usually an unclosed quote, a mismatched [[…]] header, a missing =, or a duplicate key. The TOML Validator points at the exact line and column.

My nested keys collided into one variable

If two different paths sanitize to the same name — for example my-key and my_key both become MY_KEY — the later line wins in whatever loader reads the file. Rename the source keys so they stay distinct after non-alphanumeric characters are replaced with _.

An array came out as a quoted JSON blob I can't use

That's the default JSON-encoded policy. Switch Arrays to Comma-joined if your loader splits on commas, or to Drop if it can't take a list at all.

A . separator broke my shell

Most shells reject a . inside an environment variable name, so export A.B=1 fails. Use __ (or _) for anything that gets exported into a shell; reserve . for loaders that parse the file themselves.

My # comments are gone

Expected. The parser builds a value tree with no comment nodes, so the emitter has nothing to write. Keep the original TOML if the comments matter.

FAQ

Why __ and not . as the default separator?

Most shells refuse a . inside an environment variable name, and __ is the nesting convention Spring Boot, .NET, and Helm already understand. It's safe everywhere a . isn't.

How are TOML datetimes represented?

As ISO 8601 strings. A flat env value is just text, so the canonical ISO form keeps the value readable and unambiguous for whatever reads it back.

Is my data uploaded?

Never. The parser and emitter run entirely in your browser; you can disconnect from the network and the conversion still works. Even so, treat the output like any .env — it holds your secrets in cleartext, so don't commit it.

How does this differ from the official tooling?

There's no standard "TOML to .env" command. This bakes the common framework convention (double-underscore nesting, key sanitizing, configurable array handling) into one client-side pass, so you don't have to script the flattening yourself.