Multistage images

This section explores multi-stage builds. There are two main reasons for why you’d want to use multi-stage builds:

  • They allow you to run build steps in parallel, making your build pipeline faster and more efficient.

  • They allow you to create a final image with a smaller footprint, containing only what’s needed to run your program.

In a Dockerfile, a build stage is represented by a FROM instruction. The Dockerfile from the previous section doesn’t leverage multi-stage builds. It’s all one build stage. That means that the final image is bloated with resources used to compile the program.

$ docker build --tag=buildme .
$ docker images buildme
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
buildme      latest    c021c8a7051f   5 seconds ago   150MB

The program compiles to executable binaries, so you don’t need Go language utilities to exist in the final image.

Add stages

Using multi-stage builds, you can choose to use different base images for your build and runtime environments. You can copy build artifacts from the build stage over to the runtime stage.

Modify the Dockerfile as follows. This change creates another stage using a minimal scratch image as a base. In the final scratch stage, the binaries built in the previous stage are copied over to the filesystem of the new stage.

  # syntax=docker/dockerfile:1
  FROM golang:1.20-alpine
  WORKDIR /src
  COPY go.mod go.sum .
  RUN go mod download
  COPY . .
  RUN go build -o /bin/client ./cmd/client
  RUN go build -o /bin/server ./cmd/server
+
+ FROM scratch
+ COPY --from=0 /bin/client /bin/server /bin/
  ENTRYPOINT [ "/bin/server" ]

Now if you build the image and inspect it, you should see a significantly smaller number:

$ docker build --tag=buildme .
$ docker images buildme
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
buildme      latest    436032454dd8   7 seconds ago   8.45MB

The image went from 150MB to only just 8.45MB in size. That’s because the resulting image only contains the binaries, and nothing else.

Further reading

We can further improve the build process by adopting parallelism. You can read more about it here

Last updated