Native .env File Support

--env-file arrived in Node 20.6.0 and lost its experimental label in 24.10.0 and 22.21.0, so on every supported release line — 26.9.0 Current, 24.21.0 Active LTS, 22.23.2 Maintenance LTS — a process reads a .env file with no dependency at all. The parser is deliberately small: one KEY=value per line, # starts a comment, single, double or backtick quotes are stripped, a leading export is ignored, and a double-quoted value may span lines.

.env — checked-in defaults for a serviceJavaScript
# service defaults, safe to commit
PORT=3000
LOG_LEVEL=info
export SERVICE_NAME="orders-api"   # the export keyword is ignored
GREETING='hello # not a comment'
show.js — reading what the file providedJavaScript
const e = process.env;
console.log(e.PORT, e.LOG_LEVEL, e.SERVICE_NAME, JSON.stringify(e.GREETING));
Output
$ node --env-file=.env show.js
3000 info orders-api "hello # not a comment"
$ node --env-file=.env --env-file=.env.development show.js
4000 debug orders-api "hello # not a comment"
$ node --env-file=.env.production show.js
node: .env.production: not found
$ node --env-file=.env --env-file-if-exists=.env.production show.js
.env.production not found. Continuing without it.
3000 info orders-api "hello # not a comment"

Pass the flag more than once and later files win, which is the layering a per-environment override needs. The real environment beats every file, so a value injected by your platform is never clobbered by a stale .env baked into the image. A missing file is a hard error, which is right for .env.production on a server and wrong for a teammate's .env.local; hence --env-file-if-exists.

The parser stops short of what dotenv 20,543 users expect: no interpolation, no command substitution, no encryption.