Skip to main content

Multi-stage Docker build with target flag

This blog post might be outdated!
This blog post was published more than one year ago and might be outdated!
· 2 min read
Stephan Hochdörfer

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.