Stage Ordering

Ordering stages to be more efficient

One common issue with complex Dockerfiles is that your final image might require additional packages installed. If this is a lot it can slow down your build.

A technique here is to break up your final image, create a stage early on in your Dockerfile which prepares the final image, installing any required packages, then your compilation stages.

The last stage will be based on the first stage and installs your build artefacts.

The benefit of this is that whilst you are developing your image the first stages are cached by docker, so unless something changes they don't get run again and later stages then use the cache.

For example:

 1FROM debian:11-slim AS base
 2WORKDIR /root
 3RUN apt-get update &&\
 4    apt-get install -y ca-certificates chromium nodejs npm &&\
 5    npm install npm@latest -g &&\
 6    npm install -g postcss postcss-cli &&\
 7    npm install autoprefixer &&\
 8    chmod -R +rx /root &&\
 9    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ~/.npmrc
11FROM golang:alpine AS build
12RUN apk add --no-cache tzdata
14WORKDIR /work
16RUN go env -w GOFLAGS=-mod=mod
17COPY go.mod .
18RUN go mod download
20COPY src/ src/
21RUN CGO_ENABLED=0 go build -o /dest/exampleapp src/bin/main.go
23FROM base AS final
24COPY --from=build /dest/* /usr/local/bin/
25WORKDIR /work

Here we now have three stages:

  • Lines 1…9 is the first stage based on debian:11-slim and we install chromium and nodejs.
  • Lines 11…21 is the second stage, which compiles our application using go.
  • Lines 23…25 is the third and last stage forming our final image. Here it uses the first stage as the base and just copies the built application from the second stage into /usr/local/bin

The main benefit here is that if you change something in the source, only those steps after the COPY src/ src/ line will be run, everything else is in the cache.

Last modified October 29, 2021: Add package installation notes (b55ebb4)