The past week I took a closer look at the several options on how to run a PHP app with Docker. In the past I’ve ran a few pre-built containers, but now I wanted to truly get to the bottom of it all as I don’t always need a full blown container with all extensions, but want to start from a rather clean base and build upon that. This post mainly functions as a note to my future self and lists projects that I found interesting during my research.
ℹ️ I was only looking to run a PHP app, without any other services (MySQL, Redis, etc) so these are not covered here. If you’re using
docker-compose it’s “only” a matter of also adding those and exposing them over the (internal) network.
This one uses
docker-compose which starts up a
php:fpm container and an
nginx container with a network between them. Nginx is configured to pass requests for PHP files to
php-fpm. This is how many articles tackle this.
This one also uses
php-fpm and Nginx but installs them from scratch and stores them in one single container (e.g. no
docker-compose). Processes are controlled using
Official PHP Image with Apache2 and PHP. Comes with handy utils such as
docker-php-ext-enable. This gist is a good reference to installing many extensions
FROM php:7.4-apache RUN apt-get update && apt-get install -y \ libfreetype6-dev \ libjpeg62-turbo-dev \ libpng-dev \ && docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install -j$(nproc) gd RUN apt-get update && apt-get install -y \ zlib1g-dev \ libicu-dev \ g++ \ && docker-php-ext-configure intl \ && docker-php-ext-install intl # … RUN a2enmod rewrite
Apache Modules are enabled using the common
a2enmod. Log and error files are symlinked to
Starts with an
alpine image and installs Apache and PHP into them without too much cruft. Also symlinks the log and error files on startup (not from the
Also installs Composer quite cleverly by leveraging its command line options.
RUN curl -sS https://getcomposer.org/installer | php -- \ --install-dir=/usr/local/bin --filename=composer
This is the official Composer Image which contains PHP and Composer. Instead of installing
composer.phar in your own container, you use this separate container to run
composer install in, and then copy its output (e.g. the
vendor folder) into the “main” container.
This is what Docker calls multi-stage builds:
With multi-stage builds, you use multiple
FROMstatements in your
FROMinstruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artefacts from one stage to another, leaving behind everything you don’t want in the final image.
It goes like this:
# COMPOSER FROM composer:1.9.1 as composer COPY composer.json composer.lock /app/ RUN composer install \ --ignore-platform-reqs \ --no-interaction \ --no-plugins \ --no-scripts \ --prefer-dist \ --optimize-autoloader \ -vvv # WEBSERVER FROM php:7.4-apache # … COPY --chown=www-data:www-data --from=composer /app/vendor/ /var/www/vendor/ # …
This way you don’t pollute your “main” container with Composer itself, as that is not needed to run your application.