Use npm ci for Clean, Reproducible Dependency Installs
Use npm ci for clean, reproducible dependency installs. Learn prerequisites, exact steps, verification, and recovery when the lockfile is out of sync.
09 Sept 2025, 15:00 UTC

When you run npm install in a Node.js project, the result can vary between machines and over time. The package.json file typically specifies semver ranges like ^18.2.0, so npm may install a newer patch or minor version that still satisfies the range. Worse, npm install can silently update package-lock.json, introducing changes that make your CI build differ from your local environment. The command npm ci solves this by installing dependencies exactly as recorded in the lockfile, without modifying it.
This guide walks you through using npm ci for deterministic dependency installation, including prerequisites, the exact steps, how to verify the result, and what to do when something goes wrong.
What npm ci Does Differently
npm ci (introduced in npm 5.7.0) performs a clean install:
- Deletes the existing
node_modulesdirectory entirely. - Installs every package exactly as specified in
package-lock.json— no version resolution, no range matching. - Does not modify
package-lock.jsonorpackage.json. - Is generally faster than
npm installin a fresh CI environment because it skips dependency resolution and uses the lockfile directly.
This makes it the recommended command for CI/CD pipelines, deployment scripts, and any situation where you need a reproducible build.
Prerequisites
Before you run npm ci, ensure the following:
- Node.js and npm installed — npm version 5.7.0 or higher. Check with
npm --version. - A
package-lock.jsonfile in your project root. This is generated automatically bynpm installand should be committed to version control. - The lockfile is in sync with
package.json. If you've added, removed, or changed a dependency inpackage.jsonwithout updating the lockfile,npm ciwill fail with an error likenpm ci can only install packages when your package.json and package-lock.json are in sync.
If you don't have a lockfile yet, run npm install once to generate it, then commit it.
Procedure: Running npm ci
Open a terminal in your project root (where package.json lives) and run:
npm ci
That's it. npm will:
- Remove the existing
node_modulesfolder (if present). - Install all dependencies listed in
package-lock.jsonwith exact versions. - Exit with code 0 on success.
For a dry run that checks for errors without touching node_modules, use:
npm ci --dry-run
This simulates the install and reports any issues (like an out-of-sync lockfile) without modifying your project.
Example: CI Pipeline Step
In a GitHub Actions workflow, you might have:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm test
The npm ci step ensures every build uses the exact dependency versions from the lockfile.
Expected Checks and Verification
After running npm ci, verify the install succeeded:
- Check the exit code — it should be 0. In a shell, you can echo it with
echo $?(Linux/macOS) orecho %ERRORLEVEL%(Windows). - Confirm
node_modulesexists and contains your dependencies:ls node_modules(ordiron Windows). - Validate the dependency tree with
npm ls. This command prints the installed tree and exits with an error if any package is missing or has an invalid version. A clean output means everything matches the lockfile. - Ensure the lockfile was not modified — in a version-controlled project, run
git status. You should see no changes topackage-lock.jsonorpackage.json. If you see modifications, something else (like a postinstall script) altered them.
For a thorough reproducibility check, run npm ci in a clean environment such as a Docker container or a fresh CI job. Compare the output of npm ls across runs — it should be identical.
Recovery Options When npm ci Fails
The most common failure is an out-of-sync lockfile. Here's how to fix it:
- Lockfile out of sync with package.json: Run
npm installto update the lockfile to match yourpackage.json. Then commit the updated lockfile and rerunnpm ci. Do not edit the lockfile manually. - Missing lockfile: If you don't have a
package-lock.json, runnpm installonce to generate it. Commit it immediately. - Corrupted
node_modules: Sincenpm cideletes the folder, a corrupted install is usually resolved by simply rerunningnpm ci. If that fails, deletenode_modulesmanually (rm -rf node_moduleson Linux/macOS,rmdir /s node_moduleson Windows) and runnpm ciagain. - npm version too old: If you're on npm 5.6 or earlier, upgrade npm with
npm install -g npm@latest.
Remember that npm ci is destructive to node_modules — it always removes it first. If you have local modifications inside node_modules (which you shouldn't, since it's generated), they will be lost. For local development with frequent dependency changes, npm install is still the right tool because it updates the lockfile and installs incrementally.
npm ci vs npm install: Quick Comparison
| Operation | npm install | npm ci |
|---|---|---|
| Reads package.json | Yes | No (uses lockfile) |
| Modifies package-lock.json | Yes (unless --no-save) | Never |
| Removes node_modules first | No | Yes |
| Version resolution | Uses ranges | Exact from lockfile |
| Best for | Local development | CI/CD, reproducible builds |
Limitations to Keep in Mind
npm cirequires a lockfile. If your project usesyarn.lockorpnpm-lock.yaml, this command won't work — use the corresponding package manager's equivalent.- The first run in a clean environment is not necessarily faster than
npm installbecause it downloads all packages from scratch. The speed advantage appears in repeated builds with cached packages. - If your project uses npm workspaces,
npm ciinstalls all workspace dependencies as defined in the lockfile, which is correct for monorepos.
By adopting npm ci in your automated workflows, you eliminate a whole class of "works on my machine" problems. The lockfile becomes the single source of truth, and every build — whether on your laptop, in CI, or in production — uses identical dependency versions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.