Dependencies
What is a lockfile?
A lockfile records the exact version of every dependency a package manager resolved, direct and transitive, so the next install gets the same tree. It is the best plan you have for a build. It is not a record of what the build actually used.
Updated
What a lockfile records
Your manifest, such as package.json or pyproject.toml, lists the packages you depend on
and the version ranges you accept. The package manager turns those ranges into exact versions for the whole tree,
including transitive dependencies, and writes the result to the lockfile.
npm's documentation describes package-lock.json as describing "the exact tree that was generated,
such that subsequent installs are able to generate identical trees".
A single entry in package-lock.json looks like this:
"node_modules/raw-body": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-...",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.6.3",
"unpipe": "1.0.0"
}
} - version is the exact version chosen.
- resolved is where it was fetched from: a registry tarball URL, or a git URL with a commit.
- integrity is a Subresource Integrity hash of the tarball, so a tampered download fails.
- dependencies are its own dependencies, which is how the tree, or dependency graph, is stitched together.
Lockfiles by package manager
| Package manager | Lockfile | Install without changing it |
|---|---|---|
| npm | package-lock.json | npm ci |
| Yarn (Berry) | yarn.lock | yarn install --immutable |
| pnpm | pnpm-lock.yaml | pnpm install --frozen-lockfile |
| uv | uv.lock | uv sync --locked |
| Poetry | poetry.lock | poetry install |
| Cargo | Cargo.lock | cargo build --locked |
| Bundler | Gemfile.lock | bundle config set frozen true |
| Go modules | go.mod versions and go.sum checksums | Build commands do not update go.mod by default |
With plain pip, the common stand-in is a requirements.txt with exact versions. It pins what it lists,
but anything it leaves out is resolved again on every install.
Use frozen installs in CI
A lockfile only helps if the build installs from it. A plain npm install can update
package-lock.json when it no longer matches package.json, so the build quietly resolves a
new tree. npm ci is built for automated environments:
- It requires a
package-lock.jsonornpm-shrinkwrap.json. - If the lockfile and
package.jsondisagree, it exits with an error instead of updating the lock. - It removes any existing
node_modulesfirst. - It never writes to
package.jsonor the lockfile.
The other commands in the table above do the same job for their package managers.
Apps and libraries
Commit the lockfile for applications and anything you deploy. For libraries, it only locks your own development
and CI: npm never publishes package-lock.json and ignores it anywhere but the root project. The
applications that install your library resolve its dependencies themselves, within your version ranges. The one
npm exception is npm-shrinkwrap.json, which is published and takes precedence, and which most
libraries should not use.
Why a lockfile is not a record of what shipped
A lockfile is the plan for one package manager in one project. The build that produced your release did more than follow that plan:
- It may not have used it. A non-frozen install, a missing lockfile or a second lockfile in a monorepo means the tree was resolved differently from the file you are looking at.
- Caches. Packages restored from a CI cache are installed without being fetched, and a cache restored by a partial key can predate the current lockfile. See what the GitHub Actions cache hides.
- Build tools. Setup actions, compilers, linters and code generators arrive through other channels, with their own dependency trees.
- Downloads and install scripts. A
curlin a build step or a package's install script fetches code no lockfile lists. - Base images. A container build pulls images and operating system packages that live outside every language lockfile.
The integrity hashes do not help with any of this. They prove that the packages in the lockfile were not tampered with; they say nothing about what the build fetched outside it. SCA tools that turn a lockfile into an SBOM inherit the same blind spots.
Record the build, keep the lockfile
Keep committing lockfiles and installing from them with frozen installs. They make builds repeatable. For the record of what a release contains, CRACI records the build itself: a package-aware proxy on its GitHub Actions runners records every package each job fetches, including packages restored from CI caches, and each job's SBOM states its own completeness. That record gives a more complete view of the dependencies in use than a scan of the lockfile. See build-time SBOMs, or book a demo to compare the two on one of your own builds.
Lockfiles: frequently asked questions
What is a lockfile?
A lockfile is a file a package manager writes to record the exact version of every dependency it resolved, direct and transitive, along with where it came from and a checksum. Later installs read it to reproduce the same tree instead of resolving version ranges again.
Should I commit my lockfile?
Yes, for applications and anything you deploy. Without a committed lockfile, every fresh install resolves version ranges again and can pick up new transitive versions. Commit it and install from it with a frozen install such as npm ci in CI.
Does a lockfile pin transitive dependencies?
Yes. That is its main job. The manifest only lists your direct dependencies and their allowed ranges; the lockfile records the exact version chosen for every package in the tree.
Do lockfiles apply to libraries I publish?
Not for your users. npm, for example, never publishes package-lock.json and ignores it anywhere but the root project. The applications that install your library resolve its dependencies themselves, within the ranges your package.json allows.
What does the integrity field in package-lock.json do?
It holds a Subresource Integrity hash, usually sha512, of the package tarball. npm checks the downloaded package against it, so a tampered tarball for a locked version fails to install. It protects the packages in the lockfile, not anything the build fetches outside it.
Is a lockfile the same as an SBOM?
No. A lockfile is an input: the plan a package manager follows for one project. An SBOM is an inventory of a release. SCA tools often turn a lockfile into an SBOM, which works when the build installed exactly the lockfile and nothing else.
Compare your lockfile with your build
Run one workflow on CRACI and set its recorded SBOM of fetched packages next to what your lockfile says.
Book a demo