/flags/npm/npm-install-omit-dev
npm install --omit=dev: install without devDependencies (replaces --production)
- tool
- npm
- command
- npm install, npm ci
- flag
- --omit=dev|optional|peer, --include=TYPE
- default
- nothing omitted ('dev' when NODE_ENV=production)
npm install --omit=dev (and npm ci --omit=dev) installs only the packages needed at runtime: everything under devDependencies, and packages reachable only through them, is left out of node_modules. It is the modern replacement for --production and --only=prod, which still work but print a deprecation warning. The lockfile is not changed; it still describes the full tree.
With and without the flag
cat > package.json <<'EOF'
{
"name": "demo",
"version": "1.0.0",
"dependencies": { "is-number": "7.0.0" },
"devDependencies": { "is-odd": "3.0.1" }
}
EOF
npm install --no-audit --no-fund >/dev/null 2>&1
echo "--- npm install"
ls node_modules
echo "--- npm ci --omit=dev"
npm ci --omit=dev --no-audit --no-fund >/dev/null 2>&1
ls node_modules
echo "--- the old flag"
npm ci --production --no-audit --no-fund 2>&1 | grep -i "warn"
echo "--- NODE_ENV=production"
NODE_ENV=production npm ci --no-audit --no-fund >/dev/null 2>&1
ls node_modulesOutput
--- npm install is-number is-odd --- npm ci --omit=dev is-number --- the old flag npm warn config production Use `--omit=dev` instead. --- NODE_ENV=production is-number
npm ci deletes node_modules first, so each run starts clean. With --omit=dev, --production or NODE_ENV=production, only is-number is installed.
Values
| Option | Effect |
|---|---|
| --omit=dev | Skip devDependencies |
| --omit=optional | Skip optionalDependencies (native add-ons that are allowed to fail) |
| --omit=peer | Do not install peer dependencies automatically |
| --omit=dev --omit=optional | The option can be repeated |
| --include=dev | Install devDependencies even when NODE_ENV=production |
| --production / --only=prod | Deprecated aliases of --omit=dev |
| omit=dev in .npmrc | Make it the default for the project or user |
If a type is both included and omitted, --include wins. That is how a build stage can override NODE_ENV=production from a base image.
In a Dockerfile
FROM node:24-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]When the image builds the app (TypeScript, Vite, Next.js), use two stages: npm ci --include=dev and the build in the first, npm ci --omit=dev plus the build output in the second. Running npm prune --omit=dev after the build is the one-stage alternative.
Pitfalls
- A package the app requires at runtime but listed under
devDependenciesworks locally and fails in production with Cannot find module. Move it withnpm install pkg --save-prod. - Lifecycle scripts such as
prepareorpostinstallthat call dev tools (husky,tsc) fail when those tools are omitted; guard them or use--ignore-scriptsfor the production install. NODE_ENV=productionset globally in CI makes everynpm installomit dev dependencies, so tests suddenly cannot find jest or vitest.npm civersusnpm installbehaviour is compared in npm ci vs npm install.
FAQ
What is the difference between --omit=dev and --production?
They do the same thing for devDependencies. --production is the old name and prints a warning telling you to use --omit=dev; --omit can also skip optional and peer dependencies.
Does NODE_ENV=production change npm install?
Yes. When NODE_ENV is production, the default of --omit becomes dev, so devDependencies are skipped even without the flag. That surprises people whose Docker build needs TypeScript or a bundler; pass --include=dev for that step.
Does --omit=dev change package-lock.json?
No. The lockfile keeps the full tree including devDependencies; the flag only decides what is written to node_modules, so the same lockfile serves development and production installs.