.env Toolkit

One paste, four jobs: format · validate · mask · convert — entirely in your browser.

Runs 100% in your browser — your .env never leaves this tab, and nothing you paste is saved.
Formatted

The formatted result appears here the moment you paste or press Demo. Every rewrite is counted in the summary line.

Sorted/normalized renames are opt-in — this pane never renames keys on its own.

How To Use

  1. Paste your .env (or .env.local, .env.production…) into the left input pane — or click Demo to load a realistic sample, or Open file to read one from disk (read locally, never uploaded). All five tabs read this one shared input; the right pane shows the active tab's result, and Save writes the formatted result back to a .env download.
  2. Format canonicalizes the file: strips BOM and CRLF, trims trailing whitespace, collapses blank-line runs, normalizes spaces around =, and completes bare keys — every action counted in the summary line. It never renames keys by default — uppercasing db_host to DB_HOST changes which variable your code reads, so renaming (uppercase, dash→underscore) sits behind the explicit "Normalize key names" switch, next to opt-in sorting and quote removal.
  3. Validate lists errors, warnings, and dialect hints, each with a clickable line number that selects the line in your input.
  4. Mask stars out values for screenshots: first/last 2 characters stay by default, short values are fully starred, keys and comments remain readable. Copy gives you the masked text; Copy raw values is the one deliberate exception and says so.
  5. Convert offers four explicit directions — env → JSON, env → YAML, JSON → env, YAML → env — plus Auto-detect that re-syncs the select to what it found. All strings by default; optional conservative type inference. For JSON ⇄ YAML directly, use the YAML to JSON Converter — routing through .env would flatten every value to a string.
  6. Compare diffs your .env against a second file (usually .env.example): keys missing on either side, and values that differ — order-insensitive, quoting-insensitive. It also catches $${VAR} references to keys that exist nowhere in the file (Validate warns on those too — under dotenvx they silently become empty strings).

Usage Example

Paste this messy file — it has the classic problems on purpose:

# Database — local development
DB_HOST = localhost
db_port=5432
API_KEY=sk-1234567890abcdefABCDEF
API_KEY=oops-duplicate
DEBUG=true
session-timeout=3600

Format rewrites it as DB_HOST=localhost / db_port=5432 / session-timeout=3600, normalizing the spaces around = and nothing else — your key names stay untouched unless you enable "Normalize key names" (which would turn them into DB_PORT / SESSION_TIMEOUT and say so). Validate flags the duplicate API_KEY as an error, suggests uppercasing the lowercase keys as a hint, and notes that DEBUG=true stays a string in dotenv. Mask turns the API key into sk**************EF while keeping the key name readable. Convert produces a JSON object in the same key order, with "DEBUG": "true" as a string unless you opt into inference.

Use Cases

  • Commit-ready cleanup: turn a hand-edited .env into consistent, diff-friendly formatting before it lands in a pull request — with a summary proving what changed.
  • Debugging config bugs: find the duplicate key, the unterminated quote swallowing half your file, or the tight # eating a password suffix — with line numbers, not guesswork.
  • Screenshot sharing: mask secrets before pasting config into issues, docs, Slack, or conference talks, without hand-editing each line.
  • Config format glue: hand a .env to a JSON-reading script or compare it against a YAML service definition — without pasting secrets into a converter that uploads them.
  • Onboarding: hand teammates a validated, formatted .env.example whose every quirk has been either fixed or explained.

Best Practices

  • Prefer quotes over cleverness. Unquoted values split the ecosystem on #, interpolation, and spacing; KEY="value" parses identically everywhere except docker run (which has no quoting at all).
  • Treat Format's summary as a changelog. If it lists actions you did not expect — especially "keys set to empty" — check the affected lines before adopting the output.
  • Sort with care. Alphabetical order is scannable, but dotenvx-style expansion evaluates in file order; the tool keeps interpolation users after their providers and tells you when it did.
  • Mask, then eyeball. The 2/2 default keeps values recognizable on purpose; if a screenshot is going somewhere hostile, drop keep-start/keep-end to 0 or enable the fixed-width mask.
  • Keep secrets out of .env entirely where you can. Masking is a display-layer defense; for committing secrets, use a dedicated secrets manager or dotenvx-style encryption instead.

Dialects at a glance

This tool parses with the Node dotenv (v16+) rules — the de-facto standard, dotenvx is a superset of it. But ".env file" is one format with at least six parsers, and they do not agree: when your file contains a construct that other dialects would read differently, the Validate tab says so line by line instead of silently choosing. The table below is the same comparison the hints are built from.

Node dotenv (v16+)

baseline — the parser this tool follows

  • Inline # in an unquoted value starts a comment even with no space before it
  • Double quotes expand \n and \r but NOT \t
  • Single quotes are fully literal
  • No variable interpolation

dotenvx (2.x)

superset — adds expansion & decryption

  • Also expands \t inside double quotes
  • Decodes \' inside single quotes
  • Expands ${VAR}, ${VAR:-default} and bare $VAR; single quotes suppress expansion

python-dotenv

Python ecosystem

  • Inline # must be preceded by whitespace; A=hello#x keeps the value
  • KEY with no = becomes null (dotenv_values) instead of being dropped
  • Single quotes still decode \\ and \'
  • Also expands \t in double quotes

docker run --env-file

plain key/values only

  • No quotes, no escapes, no multiline, no interpolation
  • # only starts a comment at line start; elsewhere it is part of the value
  • A bare KEY line reads the LOCAL shell variable of that name

Docker Compose env_file

quasi-dotenv with Compose interpolation

  • Inline # needs a space before it (unquoted) or must follow the closing quote
  • Bare KEY line = explicit unset
  • Expands $VAR / ${VAR} / ${VAR:-default}; $$ escapes a literal $

Worth knowing: docker run --env-file is the odd one out — no quotes, no escapes, no multiline values, no interpolation. A file that works perfectly under dotenv can quietly change meaning there.

Frequently Asked Questions

Is my .env file uploaded to a server or stored anywhere?

No. Parsing, formatting, validating, masking, and converting all run in your browser — the file never leaves the tab, and this page deliberately persists nothing you paste: reload it and the input is gone. Only the Mask tab remembers its numeric padding preferences (2/2, fixed-width on/off). This is the difference from tools that process env files "with a secure backend": those mean your secrets travel to their server, and from pages that claim "no telemetry" while loading analytics scripts. Here there are no third-party scripts at all, and you can verify both claims in DevTools.

Which dotenv dialect does the parser follow?

Node dotenv v16+ (the rules in its official README, unchanged through v18). It is the de-facto standard: dotenvx is a strict superset of it, and its README is the only written specification in the ecosystem. Where other dialects disagree — docker run --env-file, Compose env_file, python-dotenv — the Validate tab shows a hint explaining exactly how each one differs, instead of silently picking a behavior for you. The full comparison lives in the "Dialects at a glance" table below the tool.

Is a .env file compatible with docker run --env-file?

Only the plain subset. docker run --env-file has no quoting, no escapes, no multiline values, and no interpolation: quotes become part of the value, a # anywhere but line-start stays in the value, and a bare KEY line reads your shell's variable of that name. A dotenv file that relies on quotes or multiline values silently changes meaning under docker. The Validate tab flags every construct that would not survive the trip — and the "Dialects at a glance" table summarizes each dialect's rules.

Why does my value lose everything after the # sign?

In dotenv, a # inside an unquoted value starts a comment even when no space precedes it: DB_PASS=abc#123 becomes abc. python-dotenv and Docker Compose would keep abc#123 as the value, and docker run treats any # outside line starts as part of the value — the same file parses three different ways. When the tool detects a tight # it shows a hint; wrap the value in quotes (DB_PASS="abc#123") and every dialect agrees.

What does masking keep visible, and is it safe for screenshots?

Keys are never masked — the point of a masked file is to show which configuration exists. Values keep their first and last 2 characters with the middle replaced by equal-length asterisks (sk-1234…cdef becomes sk**********ef); values of 4 characters or fewer are fully starred because keeping 2 of 4 would reveal half. Comments stay readable. Before pasting a masked file anywhere, eyeball the output — that check is exactly why the tool shows it as plain text rather than a rasterized image.

Are comments kept when converting to JSON or YAML?

No — JSON has no comment syntax, so converting env to JSON necessarily drops them. The tool never drops silently: the summary line counts what was lost ("2 comments dropped") and points you to the Format tab when you need a comment-preserving cleanup. Converting back (JSON to env) cannot restore them, which is why Format, not Convert, is the tab for round-tripping a file you will keep as .env.

Does env→JSON turn true into a boolean?

By default no: every value stays a string, matching dotenv itself (empty values become empty strings, and dotenvx's own spec says "All environment values remain strings"). The optional "Infer types" switch converts only a conservative whitelist — exact true/false, plain decimal integers without leading zeros, and regular floats — so 0123, 0x1F, and 1_000 remain strings instead of being silently mangled. The same rule set powers our XML to JSON converter.

Can I convert DB_HOST=v into a nested YAML object?

No, and that is deliberate. No standard defines how underscores map to nesting (Spring Boot's relaxed binding accepts DEMO_ITEMPRICE as demo.item-price — with or without underscores), and the established converter direction is the opposite one: nested YAML flattened into A_B=v. Inventing an expansion rule here would break round-trips against every existing tool, so this tool keeps env→YAML flat (DB_HOST: v).

What do the validators check?

Three tiers. Errors are things that will bite you: duplicate keys (dotenv silently keeps the last one), unterminated quotes (dotenv swallows following lines into the value), invalid key names, and missing keys. Warnings are likely mistakes: empty secret slots, unquoted values containing spaces, broken ${VAR substitution braces. Hints are dialect differences: tight #, bare KEY lines, \t inside double quotes, backtick values — each explained with the affected dialects. Every finding carries a line number; clicking it selects the line in the input.

Is formatting idempotent — can I run it repeatedly while editing?

Yes. Formatting the formatted output changes nothing (locked by unit tests), so you can re-run it after every edit safely. Every action is also counted in the summary line — "3 keys uppercased, 2 extra blank lines collapsed, 1 comment kept" — so nothing is rewritten silently. Formatting never reorders keys unless you explicitly enable sorting, and sorting refuses to move keys whose values reference variables defined later in the file.

How do I convert between JSON and YAML?

Not here, on purpose. This tool is env-centric: its Convert tab offers the four directions that touch .env (env→JSON, env→YAML, JSON→env, YAML→env). JSON⇄YAML direct conversion preserves types, comments, and nesting in ways a trip through .env (where every value is a string and comments vanish) cannot — that is the dedicated YAML to JSON Converter's job. If you paste JSON or YAML here, the tool converts it to env and shows a link to the right converter for anything else.

How do I compare my .env with .env.example?

The Compare tab: paste (or Open file) your .env on the left, then paste or open the second file in the Compare pane. It reports keys missing on either side and values that differ — order-insensitive, and values are compared as parsed strings so quoting or spacing differences do not count as differences. The classic use: a teammate's app fails because a variable exists in .env.example but never made it into their .env; "missing here" names it in one glance.

Does it work offline?

After the first load, yes. The page is a static file with no third-party scripts, the parser is bundled JavaScript, and nothing phones home — disconnect your network and all four tabs keep working. There is no account, no telemetry, and no server-side component at all.