Why Server NPM Doesn't Install Dev Dependencies and How to Fix It
The Problem: Missing Dev Dependencies in Production
Have you ever deployed your Node.js application to a server, only to find that your build process fails because certain packages are missing? This is a common issue that occurs when npm skips installing devDependencies
in production environments.
By default, when you run npm install
on a server or in a production environment, npm automatically sets the NODE_ENV
to production
and skips installing packages listed in devDependencies
. While this behavior is intentional and beneficial for most cases, it can cause problems when your build process requires certain development tools.
Why This Happens
Production Optimization
The primary reason npm skips dev dependencies in production is optimization:
- Smaller Bundle Size: Production deployments don't need testing frameworks, linters, or development tools
- Faster Installation: Fewer packages mean quicker deployment times
- Security: Reduces the attack surface by not installing unnecessary packages
- Storage Efficiency: Saves disk space on production servers
Automatic Detection
NPM automatically detects production environments through:
# These conditions trigger production mode
NODE_ENV=production
npm install --production
npm install --omit=dev
Common Scenarios Where This Causes Issues
1. Build Tools Required in Production
Many modern applications require build tools during deployment:
{
"devDependencies": {
"webpack": "^5.0.0",
"typescript": "^4.0.0",
"sass": "^1.0.0",
"@types/node": "^18.0.0"
},
"scripts": {
"build": "webpack --mode=production",
"start": "node dist/server.js"
}
}
2. TypeScript Compilation
TypeScript projects often need the TypeScript compiler in production for build processes:
{
"devDependencies": {
"typescript": "^4.9.0",
"@types/express": "^4.17.0"
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
3. CSS Preprocessors
SASS, LESS, or PostCSS processors needed for stylesheet compilation:
{
"devDependencies": {
"sass": "^1.58.0",
"postcss": "^8.4.0",
"autoprefixer": "^10.4.0"
}
}
Solutions and Best Practices
Solution 1: Move Build Dependencies to dependencies
The most straightforward fix is moving build-essential packages from devDependencies
to dependencies
:
{
"dependencies": {
"express": "^4.18.0",
"typescript": "^4.9.0",
"webpack": "^5.75.0",
"sass": "^1.58.0"
},
"devDependencies": {
"jest": "^29.0.0",
"@types/jest": "^29.0.0",
"eslint": "^8.0.0"
}
}
Pros:
- Simple and reliable
- Works in all deployment scenarios
- No special configuration needed
Cons:
- Larger production bundle
- Includes tools not needed at runtime
Solution 2: Two-Stage Build Process
Implement a multi-stage deployment where you build locally or in a separate build environment:
# Local or CI build stage
npm install # Installs all dependencies including dev
npm run build
npm prune --production # Remove dev dependencies after build
# Deploy only production files
rsync -av --exclude node_modules dist/ package.json server:/app/
Solution 3: Docker Multi-Stage Build
Use Docker's multi-stage builds to separate build and runtime environments:
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install # Install all dependencies
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production # Only production dependencies
COPY /app/dist ./dist
CMD ["npm", "start"]
Solution 4: Force Install All Dependencies
Override npm's production behavior when build tools are needed:
# Install all dependencies including dev
npm install --include=dev
# Or use the older flag
npm install --also=dev
# Set environment variable
NODE_ENV=development npm install
Conclusion
Understanding why npm skips dev dependencies in production is crucial for successful deployments. The behavior exists for good reasons—optimization, security, and efficiency—but it can cause issues when build tools are needed in production.
The key is to:
- Understand your build requirements: Identify which tools are needed at build time vs runtime
- Choose the right approach: Whether it's moving dependencies, using multi-stage builds, or configuring your deployment platform
- Document your process: Make it clear how your application should be built and deployed
- Test your deployment: Always test your production build process in a staging environment
By following these practices, you can avoid the common pitfalls of missing dev dependencies while maintaining an efficient production deployment process.