Run a PHP app in a Docker Container (Notes)

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 supervisord



Official PHP Image with Apache2 and PHP. Comes with handy utils such as docker-php-ext-configure, docker-php-ext-install, and 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 /dev/stdout and /dev/stderr



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 Dockerfile).

Also installs Composer quite cleverly by leveraging its command line options.

RUN curl -sS | 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 FROM statements in your Dockerfile. Each FROM instruction 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:

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 \

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.


Did this help you out? Like what you see?
Thank me with a coffee.

I don\'t do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

BuymeaCoffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

1 Comment

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.