Multi-stage Docker build with target flag
Not being happy with our current Docker build process in one of our projects, I was researching how to optimize the process.
In almost all Docker-based projects, you have to differentiate between a dev and prod docker build. The main difference is that locally you don't want to add all application files into the container, you want the locally available files to be used instead. For a production image, you obviously need to add the built application to the container image.
That means you typically end up having 2 Dockerfiles to manage which can be annoying. Once again, I was looking for an improvement, and reading through the Docker Docs, I came across the --target
flag in multi-stage builds. With that flag, you can instruct Docker to stop at a specific build target. That sounds interesting!
That basically means you can do something like this:
ARG PHP_VERSION=8.1.4
FROM php:${PHP_VERSION}-fpm-alpine as php-dev
RUN ...
FROM php-dev as php-prod
COPY . /var/www
We have defined a php-dev
and a php-prod
multi-stage build. If we build the image without passing the --target
flag, Docker will run both build stages and we get the image that can be used for production with all the needed files. Fine.
For local (development) builds, we specify the --target php-dev
flag or set the php-dev
target in our Docker Compose file:
version: '3.4'
services:
phpfpm:
build:
context: .
dockerfile: docker/phpfpm/Dockerfile
target: php-dev
This will instruct Docker to only run the php-dev
stage of our multi-stage setup. The php-prod
stage is skipped and no files are copied into the image.
Obviously, things are not perfect. If I install dev tools (e.g. Xdebug) in the php-dev
stage, they are also part of the production image. Surely, you can disable them with some additional configuration. The main downside is that the image size is bigger, and potentially you gave an attacker a bigger attacking surface by installing more tools than needed. For now, I can live with those downsides.