
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.
โจ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(ornpm i) to add, remove, or update packages. It can modify yourpackage-lock.json.- Production/CI/CD: Use
npm cito install dependencies. It's faster, safer, and guarantees 100% reproducible builds using exactly the versions frompackage-lock.json. It never modifies yourpackage.jsonorpackage-lock.jsonfiles.
๐ Complete Comparison Table
| Aspect | npm install | npm ci |
|---|---|---|
| Speed | Average (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 |
| Cache | Uses | Optimized |
| Ideal usage | Local Development | CI/CD & Production |
๐ Detailed Explanation
npm install (or npm i)
How it works:
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_modulesWhen to use:
โ First time in project:
git clone https://github.com/your-project.git
cd your-project
npm install # Creates package-lock.jsonโ Add new dependency:
npm install lodash
npm install --save-dev typescriptโ Update dependencies:
npm install lodash@latest
npm updateโ Active development:
# Working locally
npm installBehavior with versions:
{
"dependencies": {
"next": "^15.0.3"
}
}# If 15.0.5 was released:
npm install
# May install 15.0.5 (within range ^)
# Updates package-lock.json with 15.0.5npm ci (Clean Install)
How it works:
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!):
# .github/workflows/ci.yml
- name: Install dependencies
run: npm ci # Guarantees reproducibilityโ Production builds (ALWAYS!):
# On server
git pull
npm ci
npm run buildโ Test environments:
# Docker
RUN npm ci --only=productionโ After git pull (RECOMMENDED):
git pull origin main
npm ci # Guarantees same versions as teamโ Clean problems:
# Something broke? Clean everything
npm ci # Removes node_modules and reinstallsBehavior with versions:
// package.json
{
"dependencies": {
"next": "^15.0.3"
}
}
// package-lock.json
{
"next": {
"version": "15.0.3"
}
}# 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
# 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
# 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 pushScenario 3: Pull Request Review
# 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 testScenario 4: Automated deploy
# 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 appScenario 5: Optimized 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
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"
# 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:
# DO NOT add this to .gitignore!
# package-lock.json โ NEVER!
# Always commit package-lock.jsonProblem 2: "Works on my machine, but not in CI"
# 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 pushProblem 3: npm ci is slow
# 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 ciProblem 4: Conflict between package.json and lock
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:
# โ
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 installUse npm ci when:
# โ
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):
# 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 ciis faster when there's nonode_modulesnpm installcan be faster ifnode_modulesalready exists
๐ ๏ธ Useful Related Commands
# 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๐จ Recommended Scripts
Add to your package.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 installto add deps - Commit
package-lock.jsonalways - Use
npm ciaftergit pull - Don't ignore
package-lock.jsonin git
For CI/CD:
- ALWAYS use
npm ci - Enable npm cache
- Use
--only=productionif possible - Fail build if
npm cifails
For Production:
- ALWAYS use
npm ci --only=production - Never use
npm installin production - Verify package-lock.json before deploy
- Keep Node.js updated
๐ Visual Summary
| npm install | npm ci |
|---|---|
| ๐ Development | ๐ CI/CD |
| โ Add deps | โ Production |
| ๐ Update deps | ๐ Reproduction |
| ๐จ Create lock | โก Performance |
๐ Executive Summary
For 99% of cases:
- Local development:
npm install - CI/CD and Production:
npm ci - After git pull:
npm ci - Add deps:
npm install <package> - Problems:
rm -rf node_modules && npm ci
Golden rule:
- If you will modify dependencies โ
npm install - If you will install dependencies โ
npm ci