NodeJSNODE_ENV & Environments

NODE_ENV & Environments

NODE_ENV is a conventional environment variable that tells your app — and much of the ecosystem — which environment it's running in, almost always development, production, or test. It's not special to Node itself, but Express, React, many libraries, and npm all read it to switch behavior: enabling optimizations and disabling debug aids in production, and the reverse in development. Setting it correctly (especially to production in production) has real performance and security consequences. This page covers what NODE_ENV controls, the three standard values, the costly mistake of not setting it to production, and how to branch on it sensibly.

What NODE_ENV is — and the three standard values

NODE_ENV

Used for

Typical behavior

development

Local coding

Verbose errors, stack traces, hot reload, no caching

production

Live deployment

Optimizations on, debug info off, caching, minimal errors

test

Running the test suite

Test database, deterministic settings, quiet logs

Bash
# Set it in the environment where the app runs:
NODE_ENV=production node app.js
# or via your platform's config (Docker, systemd, Kubernetes, PaaS dashboard)
`NODE_ENV` is a convention — `development`, `production`, or `test` — that the whole ecosystem reads to switch behavior
`NODE_ENV` is just an [environment variable](/nodejs/config-intro), but by **strong convention** it signals the runtime environment, and an enormous amount of code keys off it. The three standard values are **`development`** (your machine — favor debuggability), **`production`** (the live deployment — favor speed, security, and stability), and **`test`** (running automated tests — favor isolation and determinism). Stick to these three exact lowercase strings; inventing values like `staging` for `NODE_ENV` tends to break libraries that only check for `=== 'production'` (use a *separate* variable like `APP_ENV` for finer-grained environments, and keep `NODE_ENV=production` on staging so it behaves like prod). Node core itself largely ignores `NODE_ENV`; its power comes from the framework and library ecosystem agreeing to read it.
The costly mistake — forgetting production

JS
// Express, for example, changes behavior based on NODE_ENV:
//   • production  → caches view templates, terse error pages (no stack traces to clients)
//   • not set     → no view caching, FULL stack traces leaked in error responses

// A quick check at startup to catch the mistake:
if (process.env.NODE_ENV !== 'production') {
  console.warn('⚠  NODE_ENV is not "production" — running in slow/unsafe mode')
}
Not setting `NODE_ENV=production` in production is slow AND insecure — frameworks leak stack traces and skip optimizations
Forgetting to set `NODE_ENV=production` on a live server is one of the most common and damaging deployment mistakes, with two consequences. **Performance:** many libraries skip expensive optimizations and caching when `NODE_ENV` isn't `production` — historically frameworks like Express and React run measurably slower (extra checks, no template/view caching, development-only warnings) outside production mode. **Security:** development mode tends to expose internals — [Express](/nodejs/express-intro), for instance, sends **full stack traces in error responses** when `NODE_ENV` isn't `production`, handing attackers file paths, library versions, and code structure. The default when the variable is *unset* is effectively "development," so an un-configured production box runs in the slow, leaky mode. Always set `NODE_ENV=production` in your production environment, and consider a startup warning (or even a refusal) if it isn't.
Branching on the environment

JS
const isProd = process.env.NODE_ENV === 'production'
const isTest = process.env.NODE_ENV === 'test'

// Use it to choose environment-appropriate behavior:
const logger = isProd
  ? pino({ level: 'info' })                       // structured JSON, info level
  : pino({ transport: { target: 'pino-pretty' }, level: 'debug' })  // pretty, verbose

app.use(isProd ? compression() : ((req, res, next) => next()))      // compress only in prod

if (!isProd) {
  app.use(errorHandlerWithStackTraces)            // detailed errors OFF in production
}
Branch with `process.env.NODE_ENV === 'production'` to flip logging, error detail, caching, and dev-only middleware
The everyday use of `NODE_ENV` is to **switch behavior** between environments. Compute a boolean once (`const isProd = process.env.NODE_ENV === 'production'`) and branch on it to: use [pretty, verbose logging](/nodejs/pino) in development versus structured JSON at `info` level in production; show detailed error pages locally but generic messages to users in production; enable [compression](/nodejs/compression) and caching only where they matter; and mount dev-only middleware (request loggers, mock services) only when *not* in production. Compare against the **exact string** (`=== 'production'`), and prefer checking *for* production explicitly rather than checking "is it development?" — that way an unexpected/unset value fails *safe* toward production-like strictness rather than accidentally enabling debug features live. Keep environment branching to genuine cross-cutting concerns; per-value differences belong in regular [config](/nodejs/config-best-practices).
Next
Tie it together with patterns for robust, validated config: [Config Best Practices](/nodejs/config-best-practices).