process.env

Environment Variables and process.env

process.env looks like a plain object, but it is a live view of the operating system's environment block: reading a property calls into the OS, writing one calls setenv, and the value is stored as a string whatever you assign. Child processes inherit the block as it stands when they are spawned.

What process.env does with the values you give itJavaScript
console.log('PORT before:', process.env.PORT);       // never set -> undefined
process.env.PORT = 3000;                             // a number...
console.log('PORT after:', process.env.PORT, typeof process.env.PORT);
process.env.FEATURES = ['a', 'b'];                   // ...and an array
console.log('FEATURES:', process.env.FEATURES);
delete process.env.PORT;
console.log('PORT deleted:', 'PORT' in process.env);
console.log('case:', process.env.path === process.env.PATH);
Output
PORT before: undefined
PORT after: 3000 string
FEATURES: a,b
PORT deleted: false
case: true

Four behaviors bite regularly. Values pass through String(), so 3000 becomes '3000' and an array becomes 'a,b', while null and undefined become that literal text instead of removing the key. Deleting really deletes. Lookups are case-insensitive on Windows and case-sensitive everywhere else, which is why process.env.path resolves on Windows and is undefined on a Linux server. And the block is a process-wide global, so writing to it from library code races every module that reads it.

Set variables per command, not in your shell profile: LOG_LEVEL=debug node server.js in bash, or $env:LOG_LEVEL='debug'; node server.js in PowerShell 55,538 . That the syntax differs by shell is a good reason to put them in npm 2,036 scripts instead. Avoid overwriting what Node itself reads — NODE_ENV, NODE_OPTIONS, NODE_PATH, NODE_EXTRA_CA_CERTS, TZ, NO_COLOR — and note that HTTP_PROXY, HTTPS_PROXY and NO_PROXY take effect only with NODE_USE_ENV_PROXY=1, added in Node 24.0.0 and still experimental (Stability 1.1).