Finally you can skip dotenv in Node

For years, every Node project started the same way: npm install dotenv, require('dotenv').config() at the top of index.js. Node 20.6 (Sep 2023) finally shipped a built-in alternative — --env-file — and Node 22 (Apr 2024) made it stable. As of 2026, you don't need dotenv for the basic case anymore.

The flag

1node --env-file=.env index.js

Variables in .env show up on process.env. That's it.

The file format is dotenv-style key=value, not INI — there are no [sections], no export FOO=... prefix, and no shell-style ${VAR} expansion of one value referencing another. Quoted multi-line values work but with caveats; if you need anything more sophisticated, stick with dotenv.

DB_URL=postgres://localhost/app JWT_SECRET=replace-me LOG_LEVEL=debug

What's new since 20.6

  • Node 22 stabilized the flag. No more experimental warnings.
  • --env-file-if-exists (Node 22+). Same as --env-file but doesn't error when the file is missing — useful for start scripts that should run with or without a local .env.
  • process.loadEnvFile([path]) (Node 22+). Programmatic API for loading the file from JS code, handy when the path needs to be resolved at runtime. Defaults to .env in the cwd.
1import { loadEnvFile } from 'node:process' 2loadEnvFile() // loads .env from cwd 3loadEnvFile('./config/.env.test')

Multiple files and precedence

You can pass --env-file more than once:

1node --env-file=.env --env-file=.env.local index.js

Later files override earlier ones. Variables already set in the actual shell environment beat both — this catches a lot of people. If LOG_LEVEL=info is exported in your shell and LOG_LEVEL=debug is in .env, your app sees info.

NODE_OPTIONS in .env too

Useful trick: set Node CLI flags via NODE_OPTIONS in .env so they're picked up automatically by every node invocation in that directory:

NODE_OPTIONS=--enable-source-maps --inspect

What this isn't

.env files are for local development. They're not a secrets manager. In production, pull values from the orchestrator's secret store (Kubernetes secrets, Vault, AWS Secrets Manager, Railway/Fly/Vercel env vars) instead of shipping .env files.

Bun and Deno

Bun has the same --env-file flag and additionally auto-loads .env from the cwd by default — no flag needed. Deno supports --env-file too. So this isn't a Node-only innovation; it's a runtime-feature-parity moment.

Still need dotenv?

For the simple "load a file at startup" case, no. You still want a library for:

  • Variable expansion — values that reference other values. Use dotenv-expand or dotenvx, the modern fork with encryption and multi-environment support.
  • Encrypted .env files — dotenvx is the way.
  • Frontend bundles (Vite, Astro, Next, etc.). Those have their own env-loading conventions and don't use the Node runtime in production.