Skip to content

/flags/composer/composer-install-no-dev

composer install --no-dev: skip require-dev packages in production

The commands on this page have not been executed by our verification runner yet.updated
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

bash
composer install --no-dev --optimize-autoloader --no-interaction --prefer-dist

The same selection is available through the environment, which also covers other commands that install packages:

bash
COMPOSER_NO_DEV=1 composer install --optimize-autoloader

What --no-dev changes

Aspectcomposer installcomposer install --no-dev
require packagesInstalledInstalled
require-dev packagesInstalledNot installed (removed if present)
autoload-dev rulesIn the autoloaderLeft out
composer.lockRead onlyRead only, unchanged
Dependency resolutionFrom the lock fileFrom 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

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/html

Installing 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 require with composer 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-dev with composer update also works, but production should never run update; the difference is explained in composer install vs composer update.
  • When composer.json changed 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.

Sources