Back to all articles
npm install vs npm ci: The DEFINITIVE Guide!

npm install vs npm ci: The DEFINITIVE Guide!

Learn the crucial difference between npm install and npm ci and why using the right command can save your production build and CI/CD from catastrophic failures.

Human-architected research synthesized with the assistance of AI personas.
9 min read

โœจTL;DR / Executive Summary

Learn the crucial difference between npm install and npm ci and why using the right command can save your production build and CI/CD from catastrophic failures.

๐Ÿ’ก TL;DR (Too Long; Didn't Read)

  • Development: Use npm install (or npm i) to add, remove, or update packages. It can modify your package-lock.json.
  • Production/CI/CD: Use npm ci to install dependencies. It's faster, safer, and guarantees 100% reproducible builds using exactly the versions from package-lock.json. It never modifies your package.json or package-lock.json files.

๐Ÿ“Š Complete Comparison Table

Aspectnpm installnpm ci
SpeedAverage (10-30s)Fast (5-15s)
DeterministicโŒ Not guaranteedโœ… 100% reproducible
Modifies package.jsonโŒ NoโŒ No
Modifies package-lock.jsonโœ… YesโŒ Never
Requires package-lock.jsonโŒ Optionalโœ… Mandatory
Removes node_modules firstโŒ Noโœ… Yes (automatic)
Installs devDependenciesโœ… Yesโœ… Yes (can disable)
Accepts ranges (^, ~)โœ… YesโŒ No (exact only)
Adds new depsโœ… YesโŒ No
CacheUsesOptimized
Ideal usageLocal DevelopmentCI/CD & Production

๐Ÿ” Detailed Explanation

npm install (or npm i)

How it works:

bash
npm install # Internal steps: # 1. Reads package.json # 2. Reads package-lock.json (if exists) # 3. Resolves dependency tree # 4. Compares with existing node_modules # 5. Updates only what's necessary # 6. MAY modify package-lock.json # 7. Installs to node_modules

When to use:

โœ… First time in project:

bash
git clone https://github.com/your-project.git cd your-project npm install # Creates package-lock.json

โœ… Add new dependency:

bash
npm install lodash npm install --save-dev typescript

โœ… Update dependencies:

bash
npm install lodash@latest npm update

โœ… Active development:

bash
# Working locally npm install

Behavior with versions:

json
{ "dependencies": { "next": "^15.0.3" } }
bash
# If 15.0.5 was released: npm install # May install 15.0.5 (within range ^) # Updates package-lock.json with 15.0.5

npm ci (Clean Install)

How it works:

bash
npm ci # Internal steps: # 1. Checks if package-lock.json exists (REQUIRED) # 2. Removes node_modules COMPLETELY # 3. Installs EXACTLY the versions from lock # 4. NEVER modifies package-lock.json # 5. Fails if package.json and lock don't match # 6. Faster (optimized)

When to use:

โœ… CI/CD Pipelines (ALWAYS!):

yaml
# .github/workflows/ci.yml - name: Install dependencies run: npm ci # Guarantees reproducibility

โœ… Production builds (ALWAYS!):

bash
# On server git pull npm ci npm run build

โœ… Test environments:

bash
# Docker RUN npm ci --only=production

โœ… After git pull (RECOMMENDED):

bash
git pull origin main npm ci # Guarantees same versions as team

โœ… Clean problems:

bash
# Something broke? Clean everything npm ci # Removes node_modules and reinstalls

Behavior with versions:

json
// package.json { "dependencies": { "next": "^15.0.3" } } // package-lock.json { "next": { "version": "15.0.3" } }
bash
# Even if 15.0.5 exists: npm ci # Installs EXACTLY 15.0.3 (from lock)

๐ŸŽฎ Real-World Use Cases

Scenario 1: New developer on team

bash
# Day 1 - Clone project git clone https://github.com/company/project.git cd project # Option A: npm install (works) npm install # Option B: npm ci (BETTER - faster and exact) npm ci # Why npm ci? # - Faster # - Guarantees same versions as team # - Avoids "works on my machine"

Scenario 2: Add library

bash
# Need to add Express npm install express # npm ci DOES NOT work here! # npm ci doesn't add packages # After adding: git add package.json package-lock.json git commit -m "Add express" git push

Scenario 3: Pull Request Review

bash
# Download PR to test git fetch origin pull/123/head:pr-123 git checkout pr-123 # Use npm ci (guarantees exact PR deps) npm ci npm run build npm run test

Scenario 4: Automated deploy

bash
# Deploy script #!/bin/bash set -e echo "Deploying to production..." # 1. Pull latest git pull origin main # 2. Install dependencies (USE npm ci!) npm ci --only=production # Production only, faster # 3. Build npm run build # 4. Restart pm2 restart app

Scenario 5: Optimized Dockerfile

dockerfile
FROM node:20-alpine WORKDIR /app # Copy package files COPY package*.json ./ # USE npm ci in Docker! RUN npm ci --only=production # Copy code COPY . . # Build RUN npm run build CMD ["npm", "start"]

Scenario 6: GitHub Actions

yaml
name: CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node uses: actions/setup-node@v3 with: node-version: 20 cache: 'npm' # npm ci cache - name: Install dependencies run: npm ci # ALWAYS use npm ci here! - name: Type check run: npm run type-check - name: Lint run: npm run lint - name: Build run: npm run build - name: Test run: npm run test

โš ๏ธ Common Problems and Solutions

Problem 1: npm ci fails with "no such file package-lock.json"

bash
# Error: npm ci # Error: The package-lock.json file is missing # Cause: package-lock.json not committed # Solution: npm install # Creates lock git add package-lock.json git commit -m "Add package-lock.json"

Prevention:

gitignore
# DO NOT add this to .gitignore! # package-lock.json โŒ NEVER! # Always commit package-lock.json

Problem 2: "Works on my machine, but not in CI"

bash
# Dev: npm install # Installs version 15.0.5 # CI: npm ci # Installs version 15.0.3 (from old lock) # Problem: Different versions! # Solution: Always commit updated lock npm install # Updates lock git add package-lock.json git commit -m "Update dependencies" git push

Problem 3: npm ci is slow

bash
# npm ci should be faster, but it's slow? # Cause 1: Cache disabled npm config get cache # If empty, configure: npm config set cache ~/.npm --global # Cause 2: node_modules very large # npm ci removes before installing, may take time # Solution: Clean before rm -rf node_modules npm ci

Problem 4: Conflict between package.json and lock

bash
npm ci # Error: The package-lock.json was created for a different version # Cause: package.json changed but lock wasn't updated # Solution: npm install # Updates lock npm ci # Now works

๐ŸŽฏ Decision Rules

Use npm install when:

bash
# โœ… Add/remove packages npm install express npm uninstall lodash # โœ… Update dependencies npm update npm install react@latest # โœ… First setup (create lock) npm install # โœ… Active local development npm install

Use npm ci when:

bash
# โœ… CI/CD (ALWAYS!) npm ci # โœ… Production (ALWAYS!) npm ci --only=production # โœ… After cloning repository git clone ... && npm ci # โœ… After git pull git pull && npm ci # โœ… Automated tests npm ci && npm test # โœ… Docker builds RUN npm ci # โœ… Guarantee reproducibility npm ci

๐Ÿ“ˆ Performance Benchmark

Medium project (100 deps):

bash
# First installation npm install # ~25 seconds npm ci # ~15 seconds (40% faster!) # Installation with cache npm install # ~10 seconds npm ci # ~5 seconds (50% faster!) # With existing node_modules npm install # ~8 seconds (only updates) npm ci # ~15 seconds (removes all first)

Conclusion:

  • npm ci is faster when there's no node_modules
  • npm install can be faster if node_modules already exists

bash
# See what would be installed (without installing) npm install --dry-run # Install production only npm ci --only=production npm install --production # Force complete reinstall rm -rf node_modules package-lock.json npm install # See differences between package.json and lock npm ls --depth=0 # Verify lock integrity npm audit # Clean cache before installing npm cache clean --force npm ci

Add to your package.json:

json
{ "scripts": { "install:ci": "npm ci", "install:fresh": "rm -rf node_modules package-lock.json && npm install", "install:prod": "npm ci --only=production", "postinstall": "echo 'Dependencies installed successfully!'", "preinstall": "node -v && npm -v" } }

โœ… Final Checklist

For Development:

  • Use npm install to add deps
  • Commit package-lock.json always
  • Use npm ci after git pull
  • Don't ignore package-lock.json in git

For CI/CD:

  • ALWAYS use npm ci
  • Enable npm cache
  • Use --only=production if possible
  • Fail build if npm ci fails

For Production:

  • ALWAYS use npm ci --only=production
  • Never use npm install in production
  • Verify package-lock.json before deploy
  • Keep Node.js updated

๐Ÿ“š Visual Summary

npm installnpm ci
๐Ÿ“ Development๐Ÿš€ CI/CD
โž• Add depsโœ… Production
๐Ÿ”„ Update deps๐Ÿ”’ Reproduction
๐ŸŽจ Create lockโšก Performance

๐ŸŽ“ Executive Summary

For 99% of cases:

  1. Local development: npm install
  2. CI/CD and Production: npm ci
  3. After git pull: npm ci
  4. Add deps: npm install <package>
  5. Problems: rm -rf node_modules && npm ci

Golden rule:

  • If you will modify dependencies โ†’ npm install
  • If you will install dependencies โ†’ npm ci

Receive new articles

Subscribe to receive notifications about new articles directly to your email

We won't send spam. You can unsubscribe at any time.