node --env-file=.env: load environment variables without dotenv
- tool
- Node.js
- command
- node
- flag
- --env-file=FILE
- default
- none (no file is loaded)
- since
- 20.6.0
node --env-file=.env app.js reads KEY=value lines from .env and adds them to process.env before your code runs. It replaces the dotenv package for the common case. Variables that are already set in the real environment win over the file. The flag exists since Node.js 20.6; a missing file is an error, and --env-file-if-exists (Node.js 22.9+) skips it silently.
Example
cat > .env <<'EOF'
# comments and blank lines are ignored
PORT=8080
GREETING="hello world"
DB_URL=postgres://localhost/app
EOF
cat > app.js <<'EOF'
console.log(process.env.PORT, process.env.GREETING, process.env.DB_URL);
EOF
echo "--- without the flag"
node app.js
echo "--- with --env-file"
node --env-file=.env app.js
echo "--- environment wins over the file"
PORT=9000 node --env-file=.env app.js
echo "--- missing file"
node --env-file=missing.env app.js 2>&1 | head -1 | sed -E 's#^.*node(\.exe)?: #node: #' # strip the platform-specific path of nodeOutput
--- without the flag undefined undefined undefined --- with --env-file 8080 hello world postgres://localhost/app --- environment wins over the file 9000 hello world postgres://localhost/app --- missing file node: missing.env: not found
Rules
| Situation | Behaviour |
|---|---|
| Variable set in the environment and in the file | The environment value is kept |
| Several --env-file flags | Files are read in order; a later file overrides an earlier one |
| File does not exist | node exits with an error (use --env-file-if-exists to ignore) |
| Quotes around values | Removed, as GREETING shows |
| Lines starting with # | Comments |
From code: process.loadEnvFile()
process.loadEnvFile(); // reads ./.env (Node.js 21.7+ / 20.12+)
process.loadEnvFile("config.env"); // or a given pathFile format
Node.js defines its own .env syntax, documented with the DotEnv API:
| Syntax | Result |
|---|---|
| PORT=3000 | PORT is the string 3000; every value is a string |
| NAME = value | Spaces around the key and the unquoted value are trimmed |
| MSG="a = b # c" | Inside quotes, = and # are ordinary characters |
| KEY=value # note | Text after # on an unquoted line is a comment |
| export KEY=value | The export keyword is ignored, so the file can also be sourced by a shell |
| A quoted value with line breaks | The value spans several lines (Node.js 21.7+ / 20.12+) |
| 1_VAR=x | Invalid name: names use letters, digits and underscores and cannot start with a digit |
Variables that configure Node.js itself, such as NODE_OPTIONS, are applied when they come from the file, so a .env can turn on other runtime flags.
References to other variables are not expanded, and nothing is converted to a number or boolean:
cat > .env <<'EOF'
HOST=db.local
URL=postgres://${HOST}/app
DEBUG=false
EOF
node --env-file=.env -p 'process.env.URL + " | " + typeof process.env.DEBUG'Output
postgres://${HOST}/app | stringVersions
--env-file was added in Node.js 20.6.0 as an experimental flag and became stable in 24.10.0 and 22.21.0; multi-line values arrived in 21.7.0 and 20.12.0. On Node.js 18 and older the flag does not exist and node refuses to start with a "bad option" error, so projects that still support those versions keep using the dotenv package. To parse a file without touching process.env, util.parseEnv(text) returns the variables as a plain object.
When dotenv is still useful
The built-in loader covers the common case. The dotenv ecosystem adds things Node.js does not do, such as variable expansion with dotenv-expand; if you rely on it, keep the packages.
FAQ
Does --env-file override variables that are already set?
No. A variable that exists in the real environment keeps its value; the file only fills in the ones that are missing. Between several files, the later file wins.
Can I use --env-file in an npm script?
Yes, it is an ordinary node flag, so a start script of node --env-file=.env server.js works. Use --env-file-if-exists when the file is optional, for example in CI where the variables come from the pipeline.
Are numbers and booleans in .env converted?
No. Every value becomes a string in process.env, so PORT=3000 is '3000' and DEBUG=false is the non-empty string 'false'. Convert them in your code.