12.2.2.1 Node NPM Install
A focused guide to Node NPM Install, connecting core concepts with practical Docker and container operations.
Node npm install within a Docker build should generally use npm ci, rather than npm install, for a more reliable, reproducible installation that strictly follows the project's lockfile rather than potentially modifying it.
Why npm ci Is Generally Preferred Within a Build
npm ci removes any existing node_modules and installs exactly what the lockfile specifies, failing outright if the lockfile and package.json are out of sync, rather than silently reconciling them the way npm install might.
COPY package.json package-lock.json ./
RUN npm ci --production
This strict behavior is exactly what's wanted within a reproducible build process, where any unexpected lockfile mismatch should be surfaced as an explicit failure rather than silently resolved.
Why --production Matters for the Final Runtime Image
The --production flag (or the newer --omit=dev equivalent) skips installing development-only dependencies not needed for the application to actually run.
npm ci --omit=dev
This keeps the final runtime image's dependency footprint limited to only what's genuinely needed at runtime, excluding testing frameworks, linters, and other development-only tooling.
Installing Full Dependencies (Including Dev) Within a Build Stage
A build stage that needs to run a build step requiring development dependencies (a TypeScript compiler, for instance) installs the complete set, separate from the production-only install used for the final runtime image.
FROM node:20-alpine AS build
RUN npm ci
RUN npm run build
FROM node:20-alpine
RUN npm ci --omit=dev
Why Layer Ordering Still Matters With npm ci
Copying only the lockfile and package.json before running npm ci, rather than the full source code, still allows this potentially time-consuming step to be cached separately from more frequently changing application code.
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
Why Node npm Install Practices Matter
Using npm ci with appropriate production/development dependency scoping, combined with correct layer ordering for caching, produces a reliable, efficient dependency installation step appropriate for both build-time and final runtime contexts.