Dotenv files look trivial, but there is no formal standard, and the same file can load differently in different libraries. These are the rules most implementations share, and the places they diverge.
The basic form
KEY=value
- Keys are conventionally upper case with letters, digits and underscores, and should not start with a digit. Most loaders accept lowercase and some punctuation, such as dots and dashes, but sticking to
UPPER_SNAKE_CASEis the portable choice. - No spaces around the equals sign is the safest style. Some parsers allow
KEY = valueand others treat the spaces as part of the key or value. - One variable per line. Blank lines are ignored.
- Some loaders accept an optional
exportprefix, as inexport KEY=value, which makes the same file usable with a shell'ssourcecommand.
Comments
Lines beginning with # are comments:
# Database
DB_HOST=localhost
Inline comments after a value, such as PORT=3000 # default, are handled differently. Modern dotenv for Node.js and python-dotenv strip an unquoted # comment, whereas some older loaders and tools keep the comment as part of the value. If a # can appear in a value, such as in a password, quote the value.
Quoting
Quotes are optional unless the value needs protecting:
GREETING="Hello, world"
PATH_WITH_SPACES='C:\My Files'
EMPTY=
- Double quotes typically allow escape sequences such as
\nand, in many loaders, variable expansion. - Single quotes are usually literal, with no expansion or escapes.
- Unquoted values are trimmed of surrounding whitespace and end at the end of the line or at a comment.
- An opening quote must be closed. A missing closing quote is a common error, and a parser may either fail or swallow following lines.
Use quotes whenever a value contains spaces, #, quotes or leading and trailing whitespace.
Multiline values
Many loaders support a multiline value inside double quotes:
PRIVATE_KEY="-----BEGIN KEY-----
line1
line2
-----END KEY-----"
Others require literal \n escapes on a single line, and some do not support multiline at all. Check the loader's documentation, and prefer storing long keys as files referenced by path.
Variable expansion
Some tools substitute references inside values:
HOST=localhost
URL=http://${HOST}:3000
This is not part of the basic format. The original Node.js dotenv does not expand by default, and a companion package (dotenv-expand) adds it. Docker Compose and some other tools have their own expansion behaviour. A literal $ in a password can therefore be altered where expansion is on. Use single quotes or escape it.
Values are always strings
DEBUG=false gives the application the string "false", which is truthy in many languages. The application must parse booleans and numbers itself, and the accepted spellings vary.
Duplicates and precedence
If a key appears twice, most loaders use the last occurrence. Variables already set in the real environment usually win over the file in the standard dotenv libraries, so a .env value may appear to have no effect when the variable is also set in your shell or on a server.
Encoding and line endings
Use UTF-8 without a byte order mark, which can end up in the first key name. Windows CRLF line endings can leave a stray carriage return on values in some loaders. See text file encodings and LF vs CRLF.
Common mistakes
- Spaces around
= - An unclosed quote
- A
#in a password that is not quoted - Expecting
${VAR}expansion where the loader does not expand - Treating the string
"false"as a boolean - Smart quotes pasted from a document
- A line with no
=
Check the file
Docento's Text & Markdown Editor checks .env files as you type. It flags a line that is not blank, a comment or KEY=value (with an optional export), and an unclosed single or double quote, and shows the line number. It does not expand variables or know which keys your application needs.
Takeaway
Write KEY=value with no spaces around the equals sign, quote anything with spaces or #, expect values to be strings and test with the loader your app actually uses.