NodeJSBuilt-in --env-file Support

Built-in --env-file Support

Node.js 20.6 added the --env-file flag, which loads a .env file and populates process.env before your code runs — without any npm packages. This is the built-in equivalent of calling require('dotenv').config() at the top of your entry file. Node 22 extended it with --env-file-if-exists (silently ignores missing files) and support for multiline values.

Basic usage

Bash
# Load .env before running your app (Node 20.6+):
node --env-file=.env server.js

# With watch mode:
node --watch --env-file=.env server.js

# Load a non-default env file:
node --env-file=.env.staging server.js

# Multiple --env-file flags — later files override earlier ones:
node --env-file=.env --env-file=.env.local server.js

# Node 22: silently continue if the file doesn't exist:
node --env-file-if-exists=.env.local server.js
.env file format

Bash
# .env — same format as dotenv:
DATABASE_URL=postgres://localhost:5432/mydb
PORT=3000
NODE_ENV=development

# Quoted values (required for values with spaces or special characters):
GREETING="Hello, World!"
REGEX_PATTERN='^d{4}-d{2}-d{2}$'

# Comments:
# This is a comment
DATABASE_POOL_SIZE=10  # inline comment (stripped)

# Multiline values (Node 22+):
PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9...
-----END PRIVATE KEY-----"
Node's --env-file does not expand variable references (${OTHER_VAR}) — use dotenv with dotenv-expand if you need variable interpolation within .env values
The built-in `--env-file` parser handles basic key=value pairs, quoted strings, and comments, but it does **not** expand variable references like `DATABASE_URL=postgres://${DB_HOST}:${DB_PORT}/mydb`. The `dotenv` package also doesn't expand by default — you need the separate `dotenv-expand` package for that. For most use cases (environment-specific URLs, keys, flags) you don't need variable expansion, but if you do, stick with `dotenv` + `dotenv-expand` rather than the built-in flag.
--env-file vs dotenv — comparison

Feature

node --env-file

dotenv package

Install required

None — built in

npm install dotenv

Node version

20.6+

Any

Variable expansion

No

With dotenv-expand

Programmatic control

CLI flag only

dotenv.config({ path, override }) anywhere in code

Override existing env vars

No — existing env wins

override: true option available

Multiple files

Multiple --env-file flags

dotenv.config() called multiple times

Missing file behavior

Error (use --env-file-if-exists)

Silent by default

Works in all environments

Requires passing flag

Works anywhere dotenv.config() is called

--env-file does not override existing environment variables — if DATABASE_URL is already set in the process environment, the value in .env is ignored
This is the correct behavior for production: the platform-injected environment variables (from AWS Secrets Manager, Kubernetes secrets, or the hosting platform) take precedence over the `.env` file. But it can be surprising in development if you have `DATABASE_URL` exported in your shell and wonder why the `.env` value isn't being used. Check for shell exports that shadow your `.env` values with `printenv DATABASE_URL`. If you need the `.env` file to override existing variables (unusual but sometimes needed), use `dotenv` with `override: true`.
NODE_OPTIONS for scripts that call node indirectly

Bash
# If you run tests or scripts via npm/npx and they spawn node internally,
# use NODE_OPTIONS to pass the flag:
NODE_OPTIONS="--env-file=.env" npm test
NODE_OPTIONS="--env-file=.env" npx jest

# Or set it in package.json scripts:
# "test": "NODE_OPTIONS='--env-file=.env' jest"
Recommended development workflow

Bash
# .env (git-ignored) — developer's local overrides:
DATABASE_URL=postgres://localhost:5432/mydb_dev
REDIS_URL=redis://localhost:6379
PORT=3000

# .env.example (committed) — documents all required variables:
DATABASE_URL=
REDIS_URL=
PORT=3000
JWT_SECRET=

# package.json:
# "dev": "node --watch --env-file=.env src/server.js"
# "test": "NODE_OPTIONS='--env-file=.env.test' node --test src/**/*.test.js"
Commit .env.example with placeholder values — it documents required variables without exposing secrets; git-ignore .env and any .env.* files that contain real values
The `.env.example` file is documentation: it lists every variable the application needs with empty values or safe dummy values. New developers copy it to `.env` and fill in their local values. Your CI environment and production platform inject real values through their own secret management systems — they never use a `.env` file at all. This pattern keeps secrets out of version control while ensuring no one runs the app with missing configuration. Combine with a startup-time validation (Zod schema over `process.env`) to fail fast if required variables are missing.
Next
Bundle a Node.js app into a standalone binary for distribution: [Single Executable Applications](/nodejs/single-executable).