A .env file is plain text with one KEY=value pair per line. Blank lines are skipped and lines starting with # are comments. Quote a value when it holds spaces at the ends, a # sign or line breaks. There is no single standard, so dotenv for Node and Docker Compose read a few edge cases differently.
| Line | Value you get | Why |
|---|---|---|
| PORT=3000 | 3000 | Basic pair, always a string |
| EMPTY= | (empty string) | Empty values become empty strings |
| NAME= Ada | Ada | Unquoted values are trimmed at both ends |
| NAME=" Ada " | Ada | Quotes keep the spaces |
| HASH="a#b" | a#b | A # inside quotes is not a comment |
| MSG="one\ntwo" | two lines | Double quotes turn \n into a line break |
Updated
Most broken .env files fail on the same handful of details: a # in a password, a private key pasted over several lines, or a quote that never closes. Paste your file into the linter to see what dotenv would read for each key, and where Docker Compose would read it differently.
Paste your .env file
# App
PORT=3000
SITE_NAME=" My Shop "
DB_PASSWORD=abc#123
DATABASE_URL=postgres://app:$DB_PASSWORD@localhost/app
EMPTY=
PORT=8080
2FA_SECRET=xyz
PRIVATE_KEY="-----BEGIN KEY-----
MIIBOgIBAAJB
-----END KEY-----"8 keys read, 5 with notes
| Line | Key | Value | Notes |
|---|---|---|---|
| 2 | PORT | 3000 | |
| 3 | SITE_NAME | My Shop | |
| 4 | DB_PASSWORD | abc | dotenv stops at the #, but Docker Compose keeps it because no space comes before it. Quote the value. |
| 5 | DATABASE_URL | postgres://app:$DB_PASSWORD@localhost/app | Contains $. Docker Compose expands it; plain dotenv keeps it as written. |
| 6 | EMPTY | (empty string) | |
| 7 | PORT | 8080 | Also set on line 2. Keep one so nobody has to guess which wins. |
| 8 | 2FA_SECRET | xyz | Not a portable name. Shells expect letters, digits and underscores, not starting with a digit. |
| 9 | PRIVATE_KEY | -----BEGIN KEY----- MIIBOgIBAAJB -----END KEY----- | Spans several lines inside quotes. |
| Line | dotenv (Node) | Docker Compose |
|---|---|---|
| A=x#y | x | x#y (no space before #) |
| A=x #note | x | x |
| A='$HOME' | $HOME | $HOME (single quotes are literal) |
| A="$HOME" | $HOME | the value of HOME |
| A="x\ty" | x\ty | x, a tab, then y |
| A="x\ny" | two lines | two lines |
Same keys as the real file, safe dummy values, comments explaining each group. Commit this one, never the real .env.
# Server
PORT=3000
NODE_ENV=development
# Database (ask a teammate for the real password)
DB_HOST=localhost
DB_PASSWORD="change-me#please"
# Multiline values need double quotes
TLS_CERT="-----BEGIN CERTIFICATE-----
paste-here
-----END CERTIFICATE-----"config() reads .env from the current working directory by default. Values already in the environment stay as they are.
// index.js
require("dotenv").config();
const port = Number(process.env.PORT || 3000);
console.log("Listening on", port);
Inline comments are the classic trap. Compose needs whitespace before the # in an unquoted value, while dotenv cuts the value at the first # it sees. Both ignore a # that sits inside quotes.
Expansion is the other one. Compose replaces $VAR and ${VAR} inside unquoted and double-quoted values, and leaves single-quoted values untouched. Plain dotenv returns the dollar sign and name exactly as written.
A .env file usually holds database passwords and API keys, so treat it like any other secret. Keep it out of version control, give it tight file permissions on servers, and rotate any value that was ever committed by mistake.
Prefer your host's secret settings for production. The file is a convenience for local development, not a vault.
No. The format grew out of shell variable assignments and each loader documents its own rules. The dotenv package for Node and Docker Compose both publish theirs, and they agree on the basics: KEY=value lines, # comments and quoted values.
Only when you need them. Quotes are required for values with a # sign, leading or trailing spaces, or line breaks. Use single quotes in Compose when you want a $ to stay literal, because single-quoted values are not interpolated there.
Wrap it in double quotes and keep the real line breaks, which dotenv supports from version 15.0.0. You can also keep it on one line and write \n where each break goes, since double-quoted values turn \n into a newline.
Yes, with care. dotenv treats an unquoted # as the start of a comment. Docker Compose only does so when a space comes before the #, so KEY=a#b gives a#b in Compose but a in dotenv. Leave a space before inline comments to be safe.
No. By default dotenv never changes a variable that already exists in the environment, so a value set by your shell or host wins. Pass the override option set to true if you want the file to replace them.
Related: Next.js environment variables, Docker Compose env_file, Git cheat sheet, Regex cheat sheet.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.