Skip to Content
npm install vs npm ci: Same Install on Every Machine
Intermediate
0 likes
Tutorials

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.json and package-lock.json.
  • Installs what's missing in node_modules, keeps what's there.
  • If package.json asks for something the lock file doesn't have, it resolves a version and writes the lock file.
  • npm install some-package adds a package and updates both files.

npm ci (clean install)

  • Reads package-lock.json as the source of truth.
  • Deletes node_modules and installs everything fresh.
  • Never writes package.json or package-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-package isn'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 edited package.json by hand, npm install quietly 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:

  1. Pull the latest code.
  2. Run npm install. It updates the lock file to match.
  3. 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.
  4. 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 install may produce a big lock file diff with no real changes. Agree on a Node LTS version (put it in .nvmrc or the engines field of package.json) so everyone uses the same npm.
  • EBADENGINE warnings: 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 .npmrc setup. 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, then npm install name@version
  • Build server and CI: npm ci
  • "not in sync" error: npm install locally, 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
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.