Speedy Composer installs in Docker builds

I’m going to share a few optimisations you can make to speed up composer installs in Docker builds.

  1. Prestissimo
  2. Turn off Xdebug
  3. Composer install before codebase

0. Optimisation zero

There’s also an optimisation zero. I refrained from adding it to the list above because you should just be doing this anyway. Basically, commit your composer.lock. It’s important because it locks the current state (e.g. precise versions) of your dependencies. Other developers or environments should have exactly the same set of dependencies when the lock file is committed.

From a speed perspective, composer installs will be faster because Composer doesn’t have to calculate which version of dependencies it should install.

1. Prestissimo

Prestissimo is a spiffing plugin for Composer which parallelises all the downloads. You just have to install it to see sometimes large speed increases — no configuration is needed. Installing it locally is simple as:

composer global require hirak/prestissimo

To leverage it in a Docker build, install Composer in your Dockerfile with the following:

RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer && composer global require hirak/prestissimo --no-plugins --no-scripts

2. Turn off Xdebug

Composer runs much quicker with Xdebug disabled. Many Linux distributions have a CLI command called phpdismod. You can use it to disable Xdebug in your Dockerfile.

RUN phpdismod xdebug

If you’re installing PHP yourself as part of your image, you could also just not install Xdebug at all. It’s probably best to keep it out of production images anyway.

3. Composer install before codebase

Docker caches ‘layers’ within your images. When a layer changes, it invalidates the cache for all layers after it. This means a well optimised Dockerfile has things that don’t change much in base layers and stuff that changes frequently in later layers.

If you make the assumption that you change your codebase more often than your Composer dependencies — then your Dockerfile should run composer install before copying across your codebase. This will mean that your composer install layer will be cached even if your codebase changes. The layer will only be invalidated when you actually change your dependencies (composer.lock).

# Install dependencies
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer install --prefer-dist --no-scripts --no-dev --no-autoloader && rm -rf /root/.composer

# Copy codebase
COPY . ./

# Finish composer
RUN composer dump-autoload --no-scripts --no-dev --optimize

The rm -rf /root/.composer line is to remove Composer’s cache. This reduces the size of the resulting image. The location of the .composer directory may differ on the various Linux distributions.

Important: If you’ve already run Composer in your build context, your local vendor directory would overwrite the one in your build. The most convenient solution for this is to add /vendor/ to your .dockerignore file.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.