# Lockfile security: what actually helps

> Why committing a lockfile matters for supply-chain security, how npm ci differs from npm install, and the lockfile habits that reduce real exposure.

- Published: 2026-08-03
- Last updated: 2026-08-03

Lockfiles are usually framed as a reproducibility feature. They are also one of the few supply-chain controls that costs nothing and works immediately - but only if you use them in the specific way described below.

## Commit the lockfile. Always.

Without a committed lockfile, every fresh install re-resolves your declared ranges against whatever the registry holds at that moment. Your dependency tree becomes a function of *when you installed*, which means:

- Two developers on the same commit can have different code.
- CI can build something no one has ever run locally.
- A package compromised this morning enters your build this afternoon with no code change on your side.

A committed lockfile turns that into a deliberate, reviewable event: the tree only changes when someone updates the lockfile and that diff goes through review.

## Use the install command that respects it

This is the part that gets missed. Committing a lockfile does nothing if your CI runs a command that is allowed to ignore it.

- **npm** - `npm ci` installs strictly from the lockfile and fails if package.json and the lockfile disagree. `npm install` will happily rewrite the lockfile.
- **Yarn** - `yarn install --frozen-lockfile` (Classic) or `yarn install --immutable` (Berry).
- **pnpm** - `pnpm install --frozen-lockfile`. This is already the default in CI environments.
- **pip** - there is no native equivalent. The closest is a fully pinned requirements file generated by `pip freeze`, or `pip install --require-hashes` with a hash-pinned file, which is stricter still.

If your CI uses the loose form, a compromised transitive dependency can enter the build without ever appearing in a reviewed diff.

## Review lockfile diffs like code

Lockfile diffs are large and boring, which is exactly why attackers are comfortable there. You do not need to read every line - you need to notice a small set of things:

- A dependency you do not recognise appearing at all
- A package jumping several major versions in what is described as a patch bump
- The `resolved` URL pointing somewhere other than your usual registry
- A dependency count that moves far more than the change justifies

Some teams add a CI check that fails when the lockfile changes without a corresponding manifest change. It is blunt, and it catches a real class of problem.

## Where lockfiles stop helping

Being honest about the limits:

- A lockfile pins **what** you install, not **whether it is safe**. If you lock a compromised version, you have reproducibly installed malware.
- It does not stop install scripts running. That is a separate control (`ignore-scripts`).
- It does not protect the moment you update. Upgrades are exactly when a compromised release enters, and that is when review matters most.

Which is the case for combining the two habits: lock your versions so the tree only changes deliberately, and check the locked versions against known compromises so that deliberate change is an informed one.

## Frequently asked questions

### Should I commit a lockfile for a published library?

Yes, commit it - it governs your own CI and contributor installs. It is not published to consumers and does not constrain them; their resolver uses your package.json ranges.

### Is `npm ci` slower than `npm install`?

It is usually faster in CI, because it skips resolution entirely and installs from a clean slate against the lockfile.

### What is the pip equivalent of a lockfile?

There is no built-in one. `pip freeze > requirements.txt` gets you exact pins; `pip install --require-hashes` with a hash-pinned file is stronger. Poetry, PDM, and uv maintain proper lockfiles and can export to requirements.txt.

## Next step

Check your lockfile: https://dependencywatch.io/check

## Related guides

- [Does npm audit detect malicious packages?](https://dependencywatch.io/guides/does-npm-audit-detect-malware) - npm audit finds known vulnerabilities in legitimate packages. It is much weaker at finding deliberately malicious ones, and this explains why - plus what to run instead.
- [How to tell if an npm package is malicious](https://dependencywatch.io/guides/how-to-tell-if-an-npm-package-is-malicious) - Practical signals for judging whether an npm package is safe to install - what actually predicts trouble, what looks alarming but does not, and how to check before you install.

---

Canonical page: https://dependencywatch.io/guides/lockfile-security-best-practices
Markdown version: https://dependencywatch.io/guides/lockfile-security-best-practices.md
Site index for agents: https://dependencywatch.io/llms.txt
DependencyWatch.io is operated by Precursor Security (https://precursorsecurity.com).
