Why Server NPM Doesn't Install Dev Dependencies and How to Fix It

npm, DevOps|AUGUST 12, 2025|0 VIEWS
Understanding why development dependencies are skipped in production environments and how to handle build dependencies properly

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 --from=builder /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:

  1. Understand your build requirements: Identify which tools are needed at build time vs runtime
  2. Choose the right approach: Whether it's moving dependencies, using multi-stage builds, or configuring your deployment platform
  3. Document your process: Make it clear how your application should be built and deployed
  4. 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.