docker
node_env
package.json
environment-variables
script-execution

Passing NODE_ENV to docker to run package.json scripts

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

When a Node.js application changes behavior based on NODE_ENV, the important question in Docker is where that variable should be set. The answer depends on whether you need the value during image build, during container runtime, or both, because Docker treats build-time and runtime configuration as separate scopes.

Set NODE_ENV at Runtime for Script Behavior

If the goal is to control how npm run ... behaves inside a running container, the cleanest place is usually the runtime environment.

bash
docker run --rm -e NODE_ENV=development my-app:latest npm run dev

This passes NODE_ENV=development into the container process. The script can then read it through process.env.NODE_ENV.

A small diagnostic script makes this easy to verify.

json
1{
2  "scripts": {
3    "print-env": "node -e \"console.log(process.env.NODE_ENV)\""
4  }
5}

Then run:

bash
docker run --rm -e NODE_ENV=staging my-app:latest npm run print-env

That should print staging.

Use ENV in the Dockerfile for a Default

If you want the image to have a default runtime value, set it in the Dockerfile.

dockerfile
1FROM node:20-alpine
2WORKDIR /app
3
4COPY package*.json ./
5RUN npm ci
6
7COPY . .
8ENV NODE_ENV=production
9CMD ["npm", "run", "start"]

This gives the container a baseline value, but docker run -e NODE_ENV=... can still override it at runtime.

That is usually what you want: a production-safe default that can be replaced explicitly in development or staging.

Understand Build-Time Versus Runtime Scope

A frequent source of confusion is expecting a build argument to keep affecting the running container automatically. ARG and ENV are not the same thing.

dockerfile
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV

Build with:

bash
docker build --build-arg NODE_ENV=development -t my-app:dev .

ARG exists only during image build unless you copy it into ENV. If the variable only matters to the running application, a plain runtime environment variable is usually simpler.

Docker Compose Is Better for Team Workflows

For repeatable local development and staging setups, Docker Compose or another orchestrator is usually better than remembering long docker run commands.

yaml
1services:
2  web:
3    image: my-app:latest
4    environment:
5      NODE_ENV: development
6    command: ["npm", "run", "dev"]

This keeps environment configuration declarative and visible to the team.

If the project uses several services, Compose also prevents subtle drift where one container gets the right environment and another silently uses the Dockerfile default.

The same explicitness helps in CI. When a pipeline fails, it is much easier to reproduce locally if the source of NODE_ENV is visible in Compose or the runner command instead of being implied indirectly.

Be Explicit About Dependency Installation

NODE_ENV is often treated as if it automatically solves dependency installation behavior, but it is better to make build intent explicit.

dockerfile
RUN npm ci --omit=dev

This is clearer than hoping the right NODE_ENV side effect will produce the dependency tree you want. Explicit install commands are easier to audit and less surprising across environments.

Common Pitfalls

The most common mistake is setting NODE_ENV only at build time and expecting a later runtime container to inherit dynamic behavior automatically. Another is assuming a host shell variable is passed into the container without an explicit -e flag or Compose configuration.

Teams also often mix build defaults, Dockerfile ENV, Compose overrides, and ad hoc command-line overrides without documenting which one is authoritative.

Finally, NODE_ENV is not a secret. It describes environment mode, not sensitive configuration. Database passwords and tokens belong in dedicated secret or environment mechanisms, not overloaded into this variable.

Summary

  • Use runtime environment variables when NODE_ENV controls how package scripts run inside the container.
  • Use Dockerfile ENV only for sensible defaults.
  • Use ARG only for build-time needs unless you intentionally copy it into ENV.
  • Prefer Compose or orchestrator config for repeatable team setups.
  • Keep dependency-install rules explicit instead of relying on NODE_ENV side effects alone.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.