Post

NPM CI vs NPM Install: Why Your CI/CD Pipeline is Breaking

NPM CI vs NPM Install: Why Your CI/CD Pipeline is Breaking

We have all been there. A feature branch passes perfectly on your machine, but the moment the deployment pipeline runs, the build crashes.

The culprit is often a silent mismatch in your node modules. While npm install is your best friend during active development, using it in your deployment pipelines is an anti-pattern.

Here is exactly why you need to drop npm install in automated setups and start using npm ci.


The Core Difference

The main difference comes down to intent and predictability:

  • npm install (The Explorer): Designed for active local development. It reads package.json, resolves version ranges, and can update or overwrite your lockfile.
  • npm ci (The Executor): Short for “Clean Install”. It is built strictly for automated environments. It locks down your environment by ignoring package.json entirely and enforcing your package-lock.json as the absolute truth.

💥 Real-World Example: The “Works on My Machine” Bug

Let’s look at how npm install can introduce hidden breaking changes in production.

The Scenario

Imagine you are building a React application. Your package.json lists a critical utility library with a caret range:

1
2
3
"dependencies": {
  "awesome-utils": "^2.1.0"
}

Local Behavior

You run npm install. It installs version 2.1.0. Everything builds, passes your test suite, and you push your code to GitHub.

Pipeline Failure with npm install

Two hours later, the maintainers of awesome-utils release version 2.1.1, which accidentally introduces a breaking change.

If your GitHub Actions or Jenkins pipeline uses npm install, it checks the registry, notices 2.1.1 satisfies the ^2.1.0 requirement, and installs the broken update. Your production build fails, even though you didn’t change a line of code.

Pipeline Success with npm ci

If your pipeline uses npm ci instead, it ignores the new registry release. It looks directly at your committed package-lock.json file, sees that 2.1.0 was the frozen version, and installs exactly version 2.1.0. The build passes.


Key Benefits of npm ci

1. Striking Speed Improvements

Because npm ci does not spend time resolving semantic version ranges or querying the registry for updates, it downloads packages much faster. It can easily cut your build and deployment times in half.

2. Strict Validation Checkpoints

If a teammate adds a dependency to package.json but forgets to commit the updated package-lock.json, npm ci will instantly throw an error and halt the build. This stops out-of-sync configuration changes before they hit production.

3. Clean-State Isolation

When you run npm install, it leaves existing folders alone and updates incrementally. npm ci completely nukes your local node_modules folder before downloading anything. This guarantees that zero cached or leftover artifacts contaminate your clean build.


Quick Reference Summary

Featurenpm installnpm ci
Ideal ForLocal WorkstationsCI/CD, Docker, Production
Source of Truthpackage.jsonpackage-lock.json
Can Modify Lock File?YesNo (Strictly Read-Only)
Existing node_modulesModifies incrementallyDeletes and starts fresh
Resolution MismatchOverwrites quietlyThrows fatal error

When to Use Which

1
2
3
4
5
# Use this when adding packages or writing feature code locally:
npm install express

# Use this in your Dockerfiles, GitHub Actions, or deployment scripts:
npm ci

By switching to npm ci in your deployment workflows, you eliminate a massive category of random build failures and speed up your development cycle. Happy coding!

This post is licensed under CC BY 4.0 by the author.