npm install vs npm ci: Same Install on Every Machine
Use npm ci to install exactly what the lock file says and npm install to change it. What each command does, when to use which, and how to fix "npm ci can only install packages when your package.json and package-lock.json are in sync".
Use npm ci when you want to install exactly what the project already has: after cloning, on a build server, in CI. Use npm install when you want to change what the project has: adding, removing or updating a package. That's the rule. The rest of this page is why, and what to do when npm ci refuses to run.
Skip ahead: If you only need the fix for "npm ci can only install packages when your package.json and package-lock.json are in sync", skip to "When npm ci refuses".
What each one does
npm install
- Reads
package.jsonandpackage-lock.json. - Installs what's missing in
node_modules, keeps what's there. - If
package.jsonasks for something the lock file doesn't have, it resolves a version and writes the lock file. npm install some-packageadds a package and updates both files.
npm ci (clean install)
- Reads
package-lock.jsonas the source of truth. - Deletes
node_modulesand installs everything fresh. - Never writes
package.jsonorpackage-lock.json. - Stops with an error if the two files disagree, or if there's no lock file.
- Can't add a single package.
npm ci some-packageisn't a thing.
Why "same install on every machine" needs ci
Say package.json has "dayjs": "^1.11.23". The ^ allows any newer 1.x version. The lock file pins 1.11.23.
- With
npm ci, your laptop, your teammate's laptop and the build server all get 1.11.23. - With
npm install, they also get 1.11.23 as long as the lock file is in sync. But if anyone editedpackage.jsonby hand,npm installquietly resolves something and changes the lock file during the build. Now the server built something nobody tested.
npm ci turns that silent change into a loud error. That's what you want on a server.
The routine
On your machine, when you change dependencies:
npm install dayjs@latest
npm test
git add package.json package-lock.json
git commit -m "Update dayjs"
After you clone or pull someone else's changes:
npm ci
On the build server or in CI, always:
npm ci
npm test
💡 Tip: npm ci deletes node_modules every time, so it can be slower than npm install on your own machine when nothing changed. On a build server, it starts from nothing anyway, and repeatability matters more than a few seconds. Most CI services can cache npm's download cache (~/.npm) to speed it up. Cache that, not node_modules.
When npm ci refuses
You'll see this with npm 11:
npm error `npm ci` can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with `npm install` before continuing.
npm error
npm error Missing: ms@2.1.3 from lock file
The last line tells you what's out of sync. Someone added ms to package.json and committed it without the updated lock file. The fix happens on a developer machine, not on the server:
- Pull the latest code.
- Run
npm install. It updates the lock file to match. - Look at what changed in
package-lock.json(git diff package-lock.json). It should only be the package you meant, plus its own dependencies. - Run the tests, commit the lock file, push.
If there's no lock file at all, you get:
npm error The `npm ci` command can only install with an existing package-lock.json or
npm error npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or
npm error later to generate a package-lock.json file, then try again.
Same fix: run npm install locally, commit the new package-lock.json.
âš Warning: Don't "fix" either error by switching the build script from npm ci to npm install, or by deleting the lock file. Both make the error go away by removing the check. The next time a dependency publishes a broken minor version, your build will pick it up and you won't know why production changed.
Other things that look like ci problems
- Different npm versions can write the lock file slightly differently, and a teammate's
npm installmay produce a big lock file diff with no real changes. Agree on a Node LTS version (put it in.nvmrcor theenginesfield ofpackage.json) so everyone uses the same npm. EBADENGINEwarnings: a package says it needs a different Node version. Read which one, and update Node rather than ignoring it.- Private packages fail on the server with 401 or 404: the server needs its own read token in its
.npmrcsetup. Never commit a personal token to the repository.
Quick reference
- Just cloned:
npm ci - Adding a package:
npm install name - Removing one:
npm uninstall name - Updating:
npm outdated, thennpm install name@version - Build server and CI:
npm ci - "not in sync" error:
npm installlocally, commit the lock file
Look at your project's build script or CI file right now. Does it say npm ci, and is package-lock.json committed?
About
What npm install and npm ci each do, the routine for laptops and build servers, and the real fix when npm ci refuses to run. After this, your build server installs what you tested.
- Responsible
- Christopher R
- Last Update
- 09/07/2026
- Reading Time
- 5 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.