`npm install` vs `npm ci`
What it is
Section titled “What it is”npm install resolves your dependencies. It reads package.json, works out a tree
that satisfies it, installs that, and writes what it decided into
package-lock.json. If a newer version satisfies the range in package.json, it
may pick it, and the lock file changes.
npm ci does not resolve anything. It reads package-lock.json, deletes
node_modules outright, and installs exactly what the lock says. It never writes the
lock file. If package.json and the lock disagree, it refuses to run.
The ci is for continuous integration, which is where it was born. It is not
“install, but the good one” — it is a different operation with a different promise.
When you’d reach for it
Section titled “When you’d reach for it”npm install when you are changing what you depend on: adding a package,
removing one, deliberately taking a newer version.
npm ci when you are reproducing a known-good tree: a build server, a container
image, a fresh clone, a teammate’s machine, or your own machine when you want to be
certain the problem is not your node_modules.
The daily case for most developers is npm install, because most days you are
working on the project rather than reproducing it.
How to tell if you should
Section titled “How to tell if you should”Ask one question: am I trying to change the dependency tree, or reproduce it?
If you cannot answer, this one usually settles it:
Would it be a problem if
package-lock.jsonshowed up in your next commit?
If yes — you are reproducing, and you want npm ci. If a lock file change would be
the expected and intended result of what you are doing, you want npm install.
The build-server version of the question is shorter: a build that runs npm install
can produce a different tree tomorrow than it did today, from the same commit. If
that sentence bothers you, use npm ci.
How it goes wrong
Section titled “How it goes wrong”npm ci fails and the message is about sync, not about you.
npm error `npm ci` can only install packages when your package.json andpackage-lock.json are in sync.This almost always means somebody edited package.json by hand and did not run
npm install to update the lock. The fix is to run npm install once, locally, and
commit the resulting lock file. The fix is not to switch the build to
npm install, though that is what makes the error go away, which is why it happens.
npm install on a build server succeeds and lies. There is no error. The build
is green. Months later a patch release of something four levels deep changes
behaviour, and the failing build and the passing build have the same commit hash.
This is the expensive one precisely because nothing ever presented as a failure.
npm ci deletes node_modules. Entirely, every time, on purpose. If you had
something in there you were editing by hand, it is gone — and editing something in
node_modules by hand was already going to end badly.
No lock file, no npm ci. It exits rather than generating one. On a project
where package-lock.json is gitignored, this reads as npm ci being broken. The
lock file being ignored is the actual bug.
Try it
Section titled “Try it”In any project with a lock file:
git status --short package-lock.json # clean?npm installgit status --short package-lock.json # still clean?What you should see, and what it means
Usually clean both times: your lock already satisfies package.json and npm had
nothing to change.
If it comes back modified, that is worth knowing — your lock file was not in sync
with what package.json allows, and every npm install anyone runs has been quietly
producing a slightly different tree. Commit it.
Ask about
Section titled “Ask about”Three ways to take this further. Paste them as they are, or edit in your own project’s details — the context is what makes the answer worth having.
Verify the model. “I now think the difference between npm install and npm ci
is that install can rewrite package-lock.json and ci never does, so ci is for
reproducing a tree and install is for changing one. Is that the whole picture, or is
there something that matters on a team of six that I am missing?”
Break it. “Show me a small, concrete example where a CI pipeline running npm install builds green for weeks and then fails with no code change. I want the actual
sequence of events and what would appear in the build log, not just the general
principle.”
Map around it. “I understand lock files well enough to choose between install and ci. What else about lock files bites teams — merge conflicts in them, transitive dependency updates, monorepos? Give me a short list and say which one I am most likely to hit first.”
If you remember one thing
Section titled “If you remember one thing”npm install decides; npm ci obeys. Use the one whose promise you actually want.