CI and production should run composer install. It installs the exact versions in composer.lock. composer update resolves new versions from composer.json and rewrites the lock file, so it belongs in a deliberate, reviewed change, never in a deploy script.
The difference in one table
| Command | Reads | Writes composer.lock | Typical use |
|---|---|---|---|
| install | composer.lock | No (warns if stale) | Build, CI, deploy, new checkout |
| update | composer.json | Yes | Planned dependency refresh |
| update vendor/package | composer.json for that package | Yes, for it and what it needs | Targeted upgrade or fix |
The Composer docs describe update as resolving everything in composer.json, writing exact versions to the lock file, then running install. They also say the very first installation of a project is done with update, which is how the lock file is created.
Why this matters for security
If a deploy script runs update, then your production code is whatever was newest at that minute. That includes a brand new release of a dependency that nobody on your team has seen. A malicious or broken release then lands in production with no pull request, no diff and no review.
With install, a new upstream release changes nothing until someone updates the lock file on purpose.
Useful flags for build boxes
composer install --no-dev --no-interaction
composer install --dry-run
composer install --audit
no-devskips packages in require-dev, so test tools stay off production servers.no-interactionstops Composer waiting for a prompt. TheCOMPOSER_NO_INTERACTIONenvironment variable does the same on CI.dry-runshows what would happen without changing anything.auditruns an advisory check once installation finishes.
When composer.lock is out of date
If composer.json changed and the lock file did not, install shows a warning. Treat it as a failure in CI. The fix is to run update locally, review, and commit both files. The update command also has a lock option that only refreshes the lock file hash to silence the warning. Use that for a cosmetic change such as a description edit, not as a shortcut past a real dependency change.
A safe update routine
- Create a branch and run
composer update, or name one package. - Review the composer.lock diff.
- Run tests and an advisory check.
- Merge. Deploy with
install.
Try it yourself
Click through it at your own pace, or open it full screen. Watch how a dependency change in a pull request can be checked before it merges.
Where Safeguard fits
Safeguard's reachability analysis helps separate advisories your code can reach from those it cannot. You can try a free scan on your own lock file.
Quick check
Is it ever fine to run composer update in CI?
Yes, in a scheduled job that opens a pull request with the lock file diff. It is not fine as a step that deploys directly.
What does composer install do with no lock file?
It resolves versions and creates one, the same as update would. Commit it so the next install is repeatable.
Sources
Last reviewed: 19 October 2026.