Skip to content

.env File Syntax Explained: The Complete Dotenv Format Guide

The complete .env file syntax guide — quoting rules, multiline values, comments, and variable expansion, with a free client-side .env parser and validator.

Try it now: ENV Parser Convert a .env file to JSON, YAML, shell exports, Docker flags or tfvars, and catch duplicate keys and quoting mistakes before they reach a container.

There Is No Single .env File Syntax

.env has no official specification. It started as a convention in Ruby's dotenv gem, got copied into Node's dotenvpackage, then into Docker Compose, then into a dozen language-specific loaders — and each one implemented its own parser, with its own opinion on quoting, comments, and multi-line values. Most of the time the differences don't matter, because most .env files only ever contain KEY=value pairs with no punctuation worth disagreeing about. They matter the moment a value contains a #, a space, a newline, or a $— and that's exactly when a file that looks fine gets parsed differently by two tools reading the same line.

The rules below are the common ground across the major loaders (Node's dotenv, Docker Compose, Python's python-dotenv). Where a loader diverges, it's called out explicitly, because assuming one loader's behavior applies everywhere is where most of these bugs come from.

The Rules That Actually Matter

  • KEY=value, no spaces around =.This isn't enforced everywhere, but it's the convention every example follows, and a stray space before the = is invalid in some parsers and silently included in the key name in others — neither outcome is what you want.
  • An unquoted value stops at the first #. Everything from that # onward is a comment, not part of the value — even if the # appears in the middle of what looks like one continuous string. A value that legitimately contains a # (a hex color, a URL fragment, a password) has to be quoted, or it gets silently truncated.
  • Single quotes are completely literal. No escape sequences, no variable expansion — API_KEY='a\nb' is the six characters a\nb, backslash and all, not a newline. Single quotes are the right choice when the value itself might contain something that looks like an escape sequence or a variable reference and you want it left exactly alone.
  • Double quotes allow escape sequences, and sometimes variable expansion. Inside double quotes, \n becomes an actual newline character and \" becomes a literal quote. Some loaders — Docker Compose among them — also expand ${OTHER_VAR} references inside double-quoted values; plain Node dotenvdoes not, by default. Don't rely on expansion working unless you've confirmed the specific loader supports it.
  • Multi-line values need a quoted string that spans lines, not a bare line break. A private key or a multi-line certificate has to be wrapped in quotes with the newlines inside them — an unquoted value simply ends at the first line break, and whatever comes after starts being parsed as a new (probably invalid) key.
.env — correct quoting, comments, and a multi-line value
# Database
DATABASE_URL=postgres://user:pass@localhost:5432/app

# A value that contains a literal # must be quoted, or it's truncated at the #
HEX_COLOR="#1e293b"

# Single quotes: completely literal, no escapes, no expansion
RAW_TEMPLATE='Hello, ${NAME}! Line1\nLine2 stays literal'

# Double quotes: \n becomes an actual newline
GREETING="Hello,\nworld"

# Multi-line value — quoted, newlines inside the quotes
PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAT8w
-----END PRIVATE KEY-----"

Two Mistakes That Don't Look Like Mistakes

Both of the following produce a .env file that looks entirely correct on a read-through. Neither one throws an error. Both cause a bug that only shows up when the running process reads the value.

A duplicate key — the last one silently wins. There's no uniqueness constraint on .env keys, so defining PORTtwice — once near the top of the file, once further down after a copy-paste from another config — doesn't raise a warning. The loader just keeps overwriting as it reads line by line, and whichever assignment comes last in the file is the one the process gets. If the second definition was added by accident, the effective value is wrong and there's nothing in the file itself pointing at why.

Trailing whitespace after = becomes part of the value.A loader that doesn't trim automatically treats everything between = and the end of the line (or the closing quote) as the value, whitespace included. API_TOKEN=abc123   with two trailing spaces sets API_TOKEN to "abc123  ", not "abc123". It looks identical in an editor, diffs as identical in most viewers that collapse trailing whitespace, and then fails an exact string comparison — a signature check, an API auth header, a feature-flag match — at runtime, with no visible reason why.

looks fine, isn't — duplicate key and trailing whitespace
PORT=3000
LOG_LEVEL=debug
PORT=4000
API_TOKEN=abc123

→ Effective PORT: 4000 (the first PORT=3000 is silently discarded)
→ Effective API_TOKEN: "abc123  " (two trailing spaces are part of the value)
→ if (token === "abc123") fails — the file "looks" like it says abc123

Why Convert .env to JSON, YAML, or Shell Exports

A .env file is a convention, not a data format — most tools that need config don't read .env directly, they expect JSON, YAML, or a set of shell exports, and something in between has to bridge the gap. A dotenv to json conversion is what you need to feed environment config into a tool that only accepts a JSON config object. Turning the same file into YAML fits a Kubernetes ConfigMap or a CI pipeline's config block. Turning it into shell export statements lets you source it directly in a script, and turning it into docker run -e flags saves retyping every variable by hand on the command line.

An env parser built for this has to apply the quoting and comment rules above correctly, or the conversion just reproduces the same ambiguity in a new format. A trustworthy env file converter also flags the duplicate-key and trailing-whitespace problems while it converts, since those are exactly the mistakes that survive a naive line-by-line pass unnoticed.

GenKitLab's ENV Parser is that dotenv converter: paste a .env file and get JSON, YAML, shell exports, Docker -e flags, or Terraform tfvars out, with duplicate keys and quoting mistakes surfaced before the file reaches a container. It runs entirely client-side — nothing you paste is uploaded anywhere. If the target format is YAML specifically and you want the conversion rules for that format covered in depth, see YAML to JSON and YAML Formatter.

Frequently asked questions

Do you need quotes around .env values?

Only when the value contains something the parser would otherwise treat as special: a # (which starts a comment on an unquoted value), leading or trailing whitespace you want preserved, or a line break for a multi-line value. Plain alphanumeric values like PORT=3000 don't need quotes at all.

What's the difference between single and double quotes in a .env file?

Single quotes are completely literal — no escape sequences and no variable expansion, so \n stays as a backslash and an n. Double quotes process escape sequences like \n as an actual newline, and in some loaders (Docker Compose, notably) also expand ${OTHER_VAR} references. Plain Node dotenv does not expand variables by default even inside double quotes.

What happens if I define the same key twice in a .env file?

No error is raised. The loader reads the file top to bottom and each assignment overwrites the previous one, so the last occurrence of the key in the file is the value the process actually gets — silently, with nothing in the file flagging that it happened.

Why did my .env value fail an exact string comparison even though it looks correct?

The most common cause is trailing whitespace after the = that a non-trimming loader includes as part of the value. "abc123" and "abc123 " look identical to the eye but aren't equal in code — this is a frequent source of failed API token or feature-flag comparisons that look inexplicable until you inspect the raw bytes.

How do I convert a .env file to JSON or YAML?

Paste it into an env parser that understands the quoting and comment rules — GenKitLab's ENV Parser converts to JSON, YAML, shell exports, Docker -e flags, or tfvars, and flags duplicate keys and quoting problems in the process, entirely in your browser.

Can I put multi-line values, like a private key, in a .env file?

Yes, but the whole value needs to be wrapped in quotes with the line breaks inside them. An unquoted value ends at the first line break, so a private key or certificate pasted without quotes gets truncated at its first newline and whatever follows is parsed as a separate, invalid line.

Last updated