/flags/composer/composer-install-no-dev
composer install --no-dev: skip require-dev packages in production
- tool
- Composer
- command
- composer install
- flag
- --no-dev
- default
- off (require-dev packages are installed; --dev is the default behaviour)
composer install --no-dev installs the packages from composer.lock that belong to require, and skips everything under require-dev: PHPUnit, static analysers, debug bars, fixtures. The generated autoloader also leaves out the autoload-dev rules, so test namespaces are not loadable in production. It is the standard first step of a PHP production deploy, usually together with --optimize-autoloader.
Production install
composer install --no-dev --optimize-autoloader --no-interaction --prefer-distThe same selection is available through the environment, which also covers other commands that install packages:
COMPOSER_NO_DEV=1 composer install --optimize-autoloaderWhat --no-dev changes
| Aspect | composer install | composer install --no-dev |
|---|---|---|
| require packages | Installed | Installed |
| require-dev packages | Installed | Not installed (removed if present) |
| autoload-dev rules | In the autoloader | Left out |
| composer.lock | Read only | Read only, unchanged |
| Dependency resolution | From the lock file | From the lock file (dev packages are still part of it) |
Because the lock file was resolved with the dev packages included, production gets exactly the versions that were tested, even for packages shared by both sections.
In a Dockerfile
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist --no-autoloader
COPY . .
RUN composer dump-autoload --no-dev --optimize
FROM php:8.3-fpm
COPY --from=vendor /app /var/www/htmlInstalling before copying the code keeps the dependency layer cached; the autoloader is generated once the classes are there. See composer dump-autoload and -o, --optimize-autoloader.
Pitfalls
- A class from a dev package used in production code fails only after deploy with "Class not found". Move such packages to
requirewithcomposer require vendor/package. - Scripts in
composer.json(post-install-cmd) that call dev tools fail under--no-dev; guard them or pass--no-scripts. --no-devwithcomposer updatealso works, but production should never runupdate; the difference is explained in composer install vs composer update.- When
composer.jsonchanged without a new lock file, Composer warns that the lock file is not up to date; fix that before deploying.
FAQ
Does composer install --no-dev change composer.lock?
No. composer install only reads the lock file; the dev packages stay listed in it under packages-dev, they are just not installed. That is why one lock file serves development and production.
How do I remove dev packages that are already installed?
Run composer install --no-dev again. It removes packages from vendor that are not part of the selected set and regenerates the autoloader without the autoload-dev rules.
Can I set --no-dev through an environment variable?
Yes, COMPOSER_NO_DEV=1 has the same effect, which is convenient in Docker images and CI jobs that run several Composer commands.