The composer.lock File

Every time you install or update packages, Composer writes (or updates) a second file alongside composer.json: composer.lock. It looks like an implementation detail you can ignore, but understanding it is essential to getting reproducible installs across machines.

What Is composer.lock?

composer.lock is a snapshot that records the exact version of every package that was actually installed — not just the packages you listed directly in require, but every transitive dependency those packages pulled in too. Where composer.json says "any version matching this constraint is fine," composer.lock says "here is precisely which version was chosen, down to the exact commit/release."

Why Constraints Alone Aren't Enough

Imagine your composer.json requires "some/package": "^1.2". That constraint is satisfied by 1.2.0, by 1.4.7, and by 1.9.9 — any 1.x release from 1.2.0 onward. If you run composer install today, you might get 1.2.0. If a teammate clones the repo and runs it next month, after new releases have shipped, they might get 1.5.3 instead — same composer.json, different actual code running. That gap is exactly what composer.lock closes: once it exists, everyone installs the version it records, not just "a version that matches."

Should You Commit composer.lock?

Project TypeCommit composer.lock?
Applications (websites, APIs, internal tools)Yes. Every teammate and every deployment installs identical versions, avoiding "it works on my machine" surprises.
Libraries (packages meant to be installed by others)Typically no. Whoever installs your library already has their own project's lock file, which takes precedence for the final install — your library's lock file wouldn't even be consulted.

That distinction trips a lot of people up at first, but it comes down to one question: is this composer.lock the one actually controlling what gets installed? For an application, yes — commit it. For a library, the consuming project's own lock file wins, so it's conventional to leave yours out of version control (and add it to .gitignore).

composer install Reads the Lock File

When a composer.lock file is present, running composer install ignores the broader constraints in composer.json and instead installs exactly what the lock file specifies:

$ composer install

This is what makes onboarding a new teammate or deploying to a server reliable — they run one command and get the identical dependency versions you tested with. This is different from composer update, which re-resolves your constraints against the latest matching versions and rewrites the lock file — the full comparison between the two commands is next.

Next step: See exactly how composer install and composer update differ, and when to use each → composer update vs. composer install