Safeguard
Developer Tools•

npm ci or npm install: which should your CI pipeline run?

Use npm ci in CI and deploys. It installs exactly what the lockfile records and fails on a mismatch, while npm install may rewrite it. Here is why.

Safeguard Security Team
4 min read

Use npm ci in CI and deployment pipelines and keep npm install for the moment you deliberately change dependencies. The npm documentation says npm ci requires a lockfile, errors if it does not match package.json, and never writes to either file, which makes the install effectively frozen.

The differences that matter

The npm documentation lists the main differences between the two commands.

Behaviornpm installnpm ci
Needs a lockfileNoYes, package-lock.json or npm-shrinkwrap.json
Lockfile and package.json disagreeUpdates the lockfileExits with an error
Can add one packageYesNo, whole project only
Existing node_modulesReuses itRemoves it first
Writes to package.json or lockfileCanNever

The failure on a mismatch is the useful part. If someone edits a range in package.json and forgets to commit the lockfile, npm ci stops the build instead of resolving something new that nobody reviewed.

Why this is a supply chain control

A pipeline that runs npm install against a lockfile can still produce a different tree than the one you reviewed, because install is allowed to update the lockfile when it sees a reason to. A pipeline that runs npm ci can only build what is written down. When a new malicious or broken release appears inside one of your ranges, ci keeps building the version you already tested.

That protection is only as good as the lockfile review. The lockfile changes whenever someone runs install or update, so look at the diff in the pull request, the same way you would look at code.

Steps to adopt it

  1. Commit package-lock.json to the repository.
  2. Replace npm install with npm ci in CI, container builds and deploy scripts.
  3. Use the same Node and npm versions locally and in CI, since lockfile formats differ between npm generations.
  4. If you generated the lockfile with flags that change the tree shape, such as legacy peer dependency handling, pass the same flags to npm ci. The documentation suggests committing them in a project .npmrc file.
  5. Let a dedicated, reviewed pull request update the lockfile on a schedule rather than letting builds do it.
# CI step
npm ci
npm test

What npm ci does not do

It does not decide whether the locked versions are safe. It only guarantees you get the ones recorded. If a locked package has a known vulnerability, npm ci will faithfully install it every time. Pair it with scanning of the lockfile so the frozen tree is also a checked tree.

It also does not stop install scripts from running. Those are controlled separately, through npm's ignore-scripts setting, which defaults to false.

Where Safeguard fits

Safeguard's dependency scanning and SBOM generation can check the tree your lockfile describes, so a frozen install is also a known one. You can run a free scan at gold.safeguard.sh/scan.

Try it yourself

Step through the pull request gate and notice where a lockfile change would be inspected before merge.

Click through it at your own pace, or open it full screen.

Quick check

Can npm ci fix a broken lockfile?

No. It errors out when the lockfile and package.json disagree. Run npm install locally, review the new lockfile, and commit it.

Is npm ci slower than npm install?

Not necessarily. The documentation example shows it finishing faster, because it skips resolution and starts from a clean node_modules. Timings vary by project.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.