The .env file format
A .env file stores configuration as one KEY=value assignment per line,
loaded into a process’s environment at startup. The format looks trivial, but
quoting, comments and interpolation differ subtly between loaders. This
reference documents the rules and includes a live linter that classifies every
line you paste.
How it works
Each non-blank, non-comment line is split at the first = into a key and a
value. The key should be UPPER_SNAKE_CASE and may not start with a digit:
# a comment line
API_KEY="sk_live_123" # double quotes preserve and can interpolate
SECRET='raw$value' # single quotes are fully literal
DB_URL=$HOST/db # $VAR expands in many loaders
export PORT=3000 # export prefix is stripped by the loader
EMPTY= # assigns an empty string
The linter applies exactly this logic: blank and # lines are ignored, a valid
key/value reports whether the value is quoted, empty or unquoted, and a line
with no = (and not a comment) is flagged as an error.
Tips and notes
- Single-quote any secret containing
$so it is not treated as interpolation. - Behaviour varies across Node dotenv, python-dotenv and docker-compose — test in your loader.
- Never commit a real
.env; ship a.env.examplewith placeholder values instead. - Keep keys UPPER_SNAKE_CASE and avoid spaces around
=for the widest compatibility.
Quoting rules compared
The three quoting modes behave differently in ways that catch developers by surprise:
No quotes — the value runs from after = to the end of the line. Leading and trailing whitespace is typically trimmed by the loader. A # character anywhere in an unquoted value starts an inline comment in most loaders, so API_KEY=abc#123 gives you abc, not abc#123. Use quotes if your value contains a hash.
Double quotes — spaces and special characters are preserved within the quotes. Most loaders support \n, \t, and other backslash escapes in double-quoted values, and expand $VAR or ${VAR} references. This is the right choice for multi-word values, values with internal spaces, or values that reference other variables.
Single quotes — the value is completely literal. No escape sequences are processed, no variable references are expanded, and no characters have special meaning. A single-quoted value that contains a dollar sign, backslash, or hash is stored exactly as written. This is the safest choice for secrets, API keys, and any value that might happen to contain characters that other modes treat specially.
Loader differences to be aware of
.env files have no single canonical specification. Different tools implement subtly different rules:
| Behaviour | Node.js dotenv | Python python-dotenv | Docker Compose |
|---|---|---|---|
export prefix stripped | Yes | Yes | Yes |
| Inline comments on unquoted values | Yes | Yes | Yes |
\n in double-quoted values | Yes | Yes | Yes |
$VAR interpolation | Optional (flag) | Optional (flag) | Yes (by default) |
| Multiline double-quoted values | Yes | Yes | Limited |
The biggest risk is $VAR interpolation. If you use Docker Compose and your .env contains a value like PASSWORD=abc$def, Docker Compose will try to expand $def as an environment variable and may silently produce PASSWORD=abc if $def is not defined. Always use single quotes for literal dollar signs in a Docker Compose environment.
Security practices
A .env file containing real credentials is a security risk if it reaches version control, an image layer, or a log file. Follow these practices:
- Add
.envto.gitignoreand verify it is not tracked:git check-ignore -v .env. - Commit only a
.env.examplewith placeholder values:API_KEY=your_key_here. - In Docker images, never
COPY .env .— use runtime environment injection from your orchestrator instead. - In CI/CD, inject secrets as masked environment variables from your secrets manager, not from a committed
.envfile. - Rotate any credential that you suspect was exposed in a commit history.
git logdoes not forget, even after agit rm.