Use Build Stages

Break up your builds into stages

One common pattern developers use for a Dockerfile is do everything at once. This might be fine for simple images but if you are compiling the application then this is bad as you can easily bloat the final image with either the source code or even the compilers needed to build but not to run.

The solution to this is to use stages. Here you break up your build into separate stages. The earlier stages do the compiling of your application whilst the last one contains your application.

The last stage always becomes the final built image.

For example:

 1FROM golang:alpine AS build
 2RUN apk add --no-cache tzdata
 3
 4WORKDIR /work
 5
 6RUN go env -w GOFLAGS=-mod=mod
 7COPY go.mod .
 8RUN go mod download
 9
10COPY src/ src/
11RUN CGO_ENABLED=0 go build -o /dest/exampleapp src/bin/main.go
12
13FROM debian:11-slim AS final
14COPY --from=build /dest/* /usr/local/bin/
15WORKDIR /work

Here we have two stages:

  • Lines 1…11 is the first stage, and it uses the golang:alpine image to compile an example application which it stores the built binary under the /dest/ directory.
  • Lines 13…15 is the second and last stage. It uses the debian:11-slim image, copies the built application from the first stage into /usr/local/bin

The final image is now a lot smaller as it doesn't have any of the compilers installed.

This is a simple example of build staging. There are other techniques you can use with this like ordering stages and intermediate stages to reduce the number of layers.