Simplify the Smallest Possible Docker Image

Docker CE 17.05 (edge release) introduces a simpler way of building images FROM scratch. It is called “Multi-Stage Builds”. In this blog, I demonstrate a simple use case of this new feature.

It was in July 2014 that I published “Create the Smallest Possible Docker Container” on the Xebia Blog. In this blog, I encouraged readers to base their images on the scratch base image whenever possible. I also demonstrated a complex, convoluted way of “creating a container that creates a container”.

Where I needed almost 1700 words to convey my message, Kelsey Hightower basically said the same thing in 6 words in his blog 12 Fractured Apps:

“Remember, ship artifacts not build environments.”

So far however, the Dockerfile format did not support this notion in an elegant way. Hence, up until now, it has never been possible to elegantly create small images as part of an “Automated Build” in Docker Hub.

Docker’s new feature called “Multi-Stage Builds” is about to change that.

Consider this Dockerfile:

FROM golang as compiler 
RUN CGO_ENABLED=0 go get -a -ldflags '-s' \
github.com/adriaandejonge/helloworld
FROM scratch 
COPY --from=compiler /go/bin/helloworld .
EXPOSE 8080
CMD ["./helloworld"]

Yes, this is a single Dockerfile with two FROM statements. Please notice two more things:

  1. “FROM golang as compiler” gives a name to the first build stage.
  2. “COPY --from=compiler /go/bin/helloworld .” refers to a file from the context of the first build stage

If you don’t provide a name, you can also refer to previous build stages by number. For example “--from=0” would have worked as well. But in my opinion that is slightly less elegant.

After running the docker build command, this is the result:

REPOSITORY        TAG      IMAGE ID       CREATED          SIZE
adejonge/simple latest 071ca07e23f5 14 minutes ago 3.9MB
<none> <none> 2471fd63f0e7 14 minutes ago 720MB
golang latest 6d0bfafa0452 2 weeks ago 703MB

The container is only 3.9MB in size. The size difference with my 2014 blog post (300KB) is explained by the fact that a new version of Go is used.

At the time of writing, this feature is very new and now available in stable Docker releases starting with 17.06 (updated: 1 Sept 2017).

Also, as of now (updated: 1 Sept 2017), Multi-Stage Builds started working in Automated Builds as part of Docker Hub. See this link for an example. So now, we can simply create the smallest possible Docker image as part of an automated build. No more tricks needed!

Thanks to Andreas Wilke from Docker for pointing out this new feature to me today.