Deterministic CI Builds & Cache Optimization

In Continuous Integration (CI) environments, you must use `npm ci` instead of `npm install`. It guarantees a reproducible build by strictly adhering to the lockfile without mutating it, and is optimized for automated environments.

Package Managers5 min readConcept 22 of 24

What is it?

npm ci stands for Clean Install. Unlike npm install, which attempts to resolve dependencies and can update package-lock.json, npm ci explicitly reads from the lockfile and fails if the package.json and lockfile are out of sync.

It also completely deletes the existing node_modules folder before installing, ensuring there are no orphaned packages from previous builds.

Why use it?

If you run npm install on a CI server, it might resolve a newer version of a transitive dependency that breaks the build, despite working on your local machine.

npm ci guarantees that the exact same bytes installed on your laptop will be installed on the build server.

How it works

In your GitHub Actions or GitLab CI pipelines, replace npm install with npm ci.

To speed up CI, you should cache the global npm cache directory (e.g. ~/.npm) using a cache key based on the hash of your package-lock.json. This prevents the CI from downloading packages from the internet on every run.

Interactive Demo

Explore the concept in action using the interactive visualization below.

CI Build Pipeline
npm ci
Deterministic
Deletes node_modules Strict lockfile read
npm install
Mutable
Resolves graph May mutate lockfile

Remember this

  • Always use npm ci in automated environments (GitHub Actions, Jenkins, Vercel).
  • npm ci guarantees deterministic builds; npm install does not.
  • npm ci deletes the node_modules folder before starting.
  • Speed up CI pipelines by caching ~/.npm using a hash of the lockfile.

Done with this concept?

Mark it complete to track your progress. No login needed.