We recently released the v2 of thecodingmachine/php docker images. It is a set of "general purpose" PHP images for Docker, with a big emphasize on developer experience.
Have a look at thecodingmachine/php. This project contains a set of Docker images containing PHP with:
- 3 variants: Apache, CLI or PHP-FPM
- NodeJS (optional, version 8 or 10)
- The most common PHP extensions (use environment variables to enable them)
- Cron (configurable via environment variables)
- ... and much more!
thecodingmachine/php v1 was a set of fat images. Fat? They contain almost all the PHP extensions available in PHP. This was very useful for a development environment, but of course, it's not optimal: the image ships with a number of PHP extensions you will never use.
thecodingmachine/php v2 brings new "slim" images. They are designed to be used as a base image for your production
images. They come with no extensions pre-loaded, but you can enable any PHP extension with a single "ARG" in your
Also, PHP 7.3 is now available in the supported versions.
|Image type||Fat||Slim (new)|
|Typical usage||Development environment, CI||Optimized production image|
|Extensions enabled by default||apcu mysqli opcache pdo pdo_mysql redis zip soap||none|
|Extensions available (compiled)||Virtually all popular extensions||none|
|Enabling an extension||Use an environment variable. i.e.:
||Add it at build time:
The usage of "fat" images is unchanged. They are designed to be used in docker-compose.
version: '3' services: my_app: image: thecodingmachine/php:7.3-v2-apache-node10 environment: # Enable the PostgreSQL extension PHP_EXTENSION_PGSQL: 1 # Disable the Mysqli extension (otherwise it is enabled by default) PHP_EXTENSION_MYSQLI: 0
Slim images are new. They are meant to be used as a base image in your
Slim images come with no extension, but you can compile any of them at build time:
ARG PHP_EXTENSIONS="apcu mysqli opcache pdo pdo_mysql redis zip soap" FROM thecodingmachine/php:7.3-v2-slim-apache # The build will automatically trigger the download and compilation # of the extensions (thanks to a ONBUILD hook in the slim image)
The build process does not only take care of running
pecl install. It does also install all the required dependencies and it
takes care of removing the unneeded development files once the build is done.
thecodingmachine/php v2 images come with all the goodies from v1. It is a "batteries included" PHP image:
- Composer is installed
nanoeditor is installed
xdebugextension, if enabled, will configure the remote host automatically (and correctly whether you use Linux, Windows or MacOS!)
Images are tagged according to PHP version, image version, variant and Node version.
thecodingmachine/php:7.3-v1-[slim]-apache-node10 ^ ^ ^ ^ ^ | | | | | PHP version | | | | | | | | Image version | | | | | | Slim / Fat | | | | Variant (apache, cli or fpm) | | Node version (empty, 8 or 10)
We have images for PHP 7.1, 7.2 and PHP 7.3.
Using only environment variables you can:
- Edit any php.ini setting
- Add/remove PHP extensions
- Add/remove Apache extensions (Apache variant only)
- Add startup scripts (for instance "
- Add Cron jobs
version: '3' services: my_app: image: thecodingmachine/php:7.3-v2-apache-node8 environment: # Let's enable the PostgreSQL extension and disable the Mysqli extension! PHP_EXTENSION_PGSQL: 1 PHP_EXTENSION_MYSQLI: 0 # set the parameter memory_limit=1g PHP_INI_MEMORY_LIMIT: 1g # set the parameter error_reporting=EALL PHP_INI_ERROR_REPORTING: E_ALL # Let's enable Webdav and SSL on Apache APACHE_EXTENSION_DAV: 1 APACHE_EXTENSION_SSL: 1 # Apache document root is the "web" directory in your project. APACHE_DOCUMENT_ROOT: web/ # On startup, let's run composer install and apply DB migrations STARTUP_COMMAND_1: composer install STARTUP_COMMAND_2: vendor/bin/doctrine orm:schema-tool:update # Finally, let's set-up cron jobs CRON_USER_1: root CRON_SCHEDULE_1: * * * * * CRON_COMMAND_1: vendor/bin/console do:stuff
The structure of the project has been deeply modified to make it more manageable.
All extensions have now a dedicated installation script.
It is way easier to create a pull request for v2 than it was for v1.
- PHP 7.1 base image is now Debian Stretch
- We dropped support for Node6. Node 8 and Node 10 are available.
Usage in continuous integration environments
Those images can be tremendously useful in continuous integration environments.
At TheCodingMachine, we are pretty fond of Gitlab CI. Our CI file now looks like this:
test: image: thecodingmachine/php:7.3-v2-cli variables: PHP_EXTENSIONS=gd event before_script: - composer install script: - vendor/bin/phpunit
Woot! So easy!
Actually, I can even replace
phpunit alone, because
./vendor/bin is part of the PATH.
That's right, anything in
vendor/bin directory can be accessed from your project's root directory... Did I say developer friendly? :)
File permissions management
Permissions management is a tricky issue when it comes to Docker. Depending on your use case (development or production), and depending on the OS you are using, you can have a wide range of issues to solve. We really tried to do our best to simplify this, without sacrificing security.
File permissions on a development environment
In a development environment, you mount your working directory into the container's
For a good development workflow:
- your IDE must have write access to the files
- your web-server should have write access to the files (for caching or upload purposes)
- scripts executed in the container (like
php-cs-fixer) should have write access too
If you are using MacOS or Windows, Docker does not really enforce any permissions in the file system. For instance, any user can modify any files owned by root on a OSX docker mount point.
For Linux users, things are more secure... and more tricky! Docker will
enforce the permissions across the mount points. If your run a
composer install in the Docker container as
root, your files
will belong to root on the host file system. This is something you want to avoid because your IDE won't be able to touch those files!
The thecodingmachine/php image solves this problem by taking the following steps:
- Out of the box, it has a
dockeruser (whose ID is 1000 by default)
- Apache is run by this
dockeruser (and not by
- On container startup, a script will first try to detect if the
/var/www/htmldirectory is mounted or not, and whether it is a Windows, MacOS, or Linux mount.
- If this is a Linux mount, it will look at the owner of the
/var/www/htmldirectory. Let's assume the directory belongs to the user "foobar" whose ID is 1001. Dynamically, the container will change the
dockerID to be 1001 (instead of 1000) by default. This is done using the
-uflag of the
- Therefore, the ID of the docker user (that is running Apache) and the ID of the mounted directory owner on the host are matching. No more permission issues while developing (Hooray!)
File permissions on a production environment
Of course, on a production environment, you don't want this. On a production environment, you will typically not use
any mount. Instead, you will copy your PHP files inside the container's
By default, the
/var/www/html directory belongs to
www-data. The container will detect this and act accordingly.
You should still give back ownership of the Apache processes to the
www-data user. This can be done easily using
one more environment variable:
ENV APACHE_RUN_USER=www-data \ APACHE_RUN_GROUP=www-data
Is this following Docker best practices?
thecodingmachine/php v1 was violating a number of Docker best-practices. In particular, it contained most of the PHP extensions, even if you did not use them. thecodingmachine/php v2 images with the slim variant are fixing this issue, so they are really better tailored for production usage.
Are they 100% state-of-the-art Docker images? Probably not.
So you want to be state-of-the-art?
Instead of using thecodingmachine/php, you should do this:
Avoid installing unnecessary packages
thecodingmachine/php v2 contains a lot of pretty useful packages for development, but that are not needed for production (like the nano editor, or Composer).
If you want to be state of the art, you should write your own
Dockerfile and install the bare-minimum in the container.
Of course, you should store the image in your own registry.
Use multi-stage builds
Some variants of thecodingmachine/php come with NodeJS installed. The expectation is that you will need NodeJS to build
your JS/CSS assets (probably using
But the image will run in production with NodeJS installed, while it is absolutely not necessary
(it is used only at build time).
Starting with Docker 17.05, Docker added this wonderful feature named multi-stage builds.
From your Dockerfile, you can call another container to perform build stages. From your PHP container, you could call a NodeJS container to perform a build and copy the built files back to your PHP container. The PHP container never stores NodeJS runtime. Useful.
Each container should have only one concern
thecodingmachine/php images bundle cron. So strictly speaking, they have 2 concerns:
- one is to answer HTTP requests
- one is to trigger events at regular intervals
If you want to be state of the art, you should delegate the scheduling of events to a separate container like Tasker or one of the other alternatives.
Here are a few image size comparison:
||460 MB||+83 MB (+22%)|
||606 MB||+229 MB (+60%)|
So should I use the thecodingmachine/php images?
Give it a try, you won't regret it!
thecodingmachine/php general purpose images help you starting quickly, while ensuring a pretty decent quality.
About the author
David is CTO and co-founder of TheCodingMachine and WorkAdventure. He is the co-editor of PSR-11, the standard that provides interoperability between dependency injection containers. He is also the lead developper of GraphQLite, a framework-agnostic PHP library to implement a GraphQL API easily.