.env & dotenv Cheatsheet - Load Order & Multi-Env

Almost every Node/Python/frontend project writes a .env, yet the most common traps are spaces around the equals sign, a # inside a value treated as a comment, and secrets accidentally committed to the repo. This reference covers 5 groups: variable definition, reading in Shell/Node/Python, dotenv loader usage, multi-environment file layout (.env/.env.local/.env.production), and a set of security-pitfall reminders. Sanity-check the format after writing and confirm no secrets were committed before deploying. After reading you can organize environment variables cleanly and avoid the most common leakage issues.

Config Formats·24 commands·Last updated 2026-08-02
envdotenvEnvironment Variablesconfig.env

Typical Use Case

For developers separating configuration and secrets from code: a .env file for local work, real environment variables in production, and a committed .env.example so the team agrees on field names. This sheet covers variable definition and reading, access syntax per language and shell, loading with dotenv, multi-environment file conventions, and four classes of security pitfalls. The single decision rule: real secrets never enter version control.

Definition 6

KEY=value
Define a variable (no spaces around =)
KEY="value with space"
Quote values with spaces or special chars
KEY='literal $NOT_EXPANDED'
Single quotes: no variable expansion
KEY=${OTHER}_suffix
Double quotes: reference other variables
# comment
Hash starts a comment line
export KEY=value
Set and export as an environment variable

Reading 5

echo $KEY
Read in shell (Linux/macOS)
echo %KEY%
Read in Windows cmd
$env:KEY
Read in PowerShell
process.env.KEY
Read in Node.js
os.getenv("KEY")
Read in Python

dotenv Usage 4

require('dotenv').config()
Node: load .env into process.env
import 'dotenv/config'
ESM import to load .env
dotenv.config({ path: '.env.local' })
Load with a custom file path
python-dotenv load_dotenv()
Python: load .env

Multi-environment Files 5

.env
Default env (don't commit to git)
.env.local
Local override (higher priority)
.env.development
Development env
.env.production
Production env
.env.example
Example template, committable, no real secrets

Security FAQ 4

Don't commit secrets
Don't commit secrets
Case sensitive
Case sensitive
Type conversion
Type conversion
Escape newlines
Escape newlines

Parameter matrix

ParameterEffectExample
KEY=valueDefines a variable; there must be no space on either side of the equals signKEY=value
KEY="a b"Quote the value when it contains spaces or special charactersKEY="value with space"
KEY='$RAW'Single quotes suppress variable expansionKEY='literal $NOT_EXPANDED'
export KEY=valueSets a variable and exports it, so child processes inherit itexport DATABASE_URL=postgres://...
$KEY / %KEY% / $env:KEYThe read syntax on the three platforms: shell, cmd and PowerShellecho $KEY
dotenv.config()Loads the .env file into the process environment inside Noderequire('dotenv').config()
import 'dotenv/config'The ESM form of loading a .env fileimport 'dotenv/config'
.env.localA local override that takes precedence over .envdotenv.config({ path: '.env.local' })
.env.<mode>One file per environment: development and production.env.production
.env.exampleA committable example template with the real secrets left outcp .env .env.example
os.getenv("KEY")Read from Python, where an unset variable comes back as Noneos.getenv("DATABASE_URL")
process.env.KEYRead from Node, where an unset variable comes back as undefinedprocess.env.PORT

Common pitfalls

SymptomThe variable is right there in the file but reads back empty.

CauseThere are spaces around the equals sign, so KEY = value parses as running the command KEY, or the variable was never exported.

FixWrite KEY=value with no spaces at all and add export so child processes can see it.

SymptomA secret made it into Git history, and deleting the file now does not help.

Cause.env was never added to .gitignore, or the ignore rule was added only after the file had already been tracked.

FixRotate the key first, since an ignore rule cannot retract leaked content; then git rm --cached .env to drop it from the index and add the .gitignore entry.

SymptomA cross-platform script fails on Windows while working fine on Linux.

CauseThe shell reads $KEY, whereas cmd needs %KEY%.

FixKeep cross-platform scripts from reading variables directly, or handle the two syntaxes separately.

SymptomThe value comes back as the string "8080" rather than a number, and comparisons blow up on the type.

CauseEnvironment values are always strings and carry no type information whatsoever.

FixConvert explicitly: wrap the PORT lookup in Number(), or hand the string to parseInt with base 10.

SymptomA multi-line value such as a private key is read back as only its first line.

CauseThe newlines were never escaped, so parsing stops at the first line break.

FixWrap it in quotes and write the real newlines as \n, or store it base64-encoded or in a secrets manager.

SymptomI edited .env.local but the application still sees the old value.

Causedotenv does not overwrite a variable that already exists in the environment by default.

FixPass override: true to dotenv.config, or make sure the load order is what you intended.

Troubleshooting

  1. 1Confirm the variable really exists in the current shell

    echo $KEY

    No output means it was never exported or .env was never loaded; set | grep KEY works as well.

  2. 2Load a file into the current session temporarily

    set -a && source .env && set +a

    set -a auto-exports everything source defines and +a turns that off; it only affects the current shell.

  3. 3Check whether Node really sees the value

    node -e "console.log(process.env.KEY)"

    Bypassing the application and asking the runtime directly separates a load failure from code that is reading the wrong field.

  4. 4Produce a secret-free template for committing

    sed -E "s/=.*/=/" .env > .env.example

    Keep the key names and blank out the values; confirm .env is listed in .gitignore.

  5. 5Check whether a secret has leaked into the repository

    git log -S "SECRET_VALUE" --oneline

    Any hit means it exists in history and the key must be rotated; editing .gitignore only protects the future.

Command Examples

Define a variable and export it for child processes

export DATABASE_URL=postgres://localhost:5432/app

No spaces around the equals sign, and a value that is merely assigned without export stays invisible to child processes.

Load a .env file from an explicit path

dotenv.config({ path: '.env.local' })

Without the path option it reads .env from the project root; supply it and only that file is loaded, with no automatic merge.

Reading syntax on each of the three platforms

echo $KEY # Linux/macOS echo %KEY% # Windows cmd $env:KEY # PowerShell

When the same configuration has to run everywhere, even the read syntax differs, so a script must not assume there is only one form.

Load and read a variable in Python

from dotenv import load_dotenv load_dotenv() os.getenv("API_HOST")

A missing variable yields None, so any check that configuration loaded properly has to handle that None case first.

Common Pitfalls

  • Keep .env out of version control and ship .env.example as the field template instead; real secrets never belong in a repository.
  • Leave no spaces around the equals sign, since KEY = value assigns nothing, and an ALL_CAPS_WITH_UNDERSCORES naming style avoids most mistakes.
  • Every value is a string, so numbers and booleans must be converted yourself; note that "false" is truthy in JavaScript and a naive check inverts your logic.
  • Names are case-sensitive, and KEY and key are two different variables on Linux and macOS, so match the case exactly while hunting for an empty value.
  • Multi-line content such as private keys or certificates must be wrapped in quotes or written with explicit escape sequences, otherwise you only ever read the first line.

Tips

  • No spaces around = (KEY=value is correct, KEY = value is wrong).
  • Add .env to .gitignore; commit secrets via .env.example template instead.
  • All values are strings; convert numbers with Number()/parseInt().
  • Multi-line values use quotes or \n for newlines.

FAQ

The variable is right there in .env but reads back empty. What gives?

Three causes account for most of these, roughly in order: a space on one side of the equals sign, which turns KEY = value into a single token rather than an assignment; the value was set but never exported, so child processes never see it; and a case mismatch, since KEY and key are two unrelated variables and the lookup is case-sensitive.

Why does my port come back as a string and blow up on comparison?

Every value in a .env file is text; there is no type annotation, so PORT=8080 yields the string "8080". Comparing or doing arithmetic on that fails at runtime unless you convert it yourself: Number(process.env.PORT) in Node, int(os.getenv("PORT")) in Python. The same trap applies to booleans, since the string "false" is truthy in JavaScript.

A multi-line value such as a private key only ever gives me the first line. Why?

A .env file is parsed line by line, so a raw newline terminates the value: the remainder becomes a new line that is either misread as another variable or dropped entirely. Wrap the value in quotes or express the line breaks with explicit escape sequences so the whole thing stays on one physical line.

If both .env and .env.local exist, which one wins?

.env.local takes precedence, acting as a local override for values only your machine should have, such as a connection string pointing at a local database. Shared baseline values belong in .env, per-environment splits go to .env.development and .env.production, and .env.example is the template the team agrees on, with no real secrets in it.

How do Node and Python actually read values from a .env file?

In Node, load the file with require('dotenv').config() first, after which values are read as process.env.KEY; in ESM projects the equivalent is the side-effect import of dotenv/config. In Python, python-dotenv's load_dotenv() populates the environment and you read through os.getenv("KEY"), which returns None when the variable is absent. In both languages the load has to happen before the read.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Aug 2, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us