TOML to .env Converter
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 getKEY: valuelines you can paste straight under a service. - Source a config into a shell. Pick shell (export KEY=val) so every line is
export KEY=valueand a singlesource out.envloads 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
MYAPPand every key comes out asMYAPP__DATABASE__HOST, so two services' configs won't collide in one environment. - Decide what happens to arrays before they hit a flat format.
.envhas 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 database → pool → size) 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.size→DATABASE__POOL__SIZE. - Key sanitizing. Each segment is re-cased per Case, and any character outside
[A-Za-z0-9_]becomes_(sokey-name→KEY_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 typetext/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.