Previously, We’ve discussed creating small Docker images, but now with Docker’s support for multi-stage builds, it’s worth revisiting the topic. Previously, we had to build the binary in one step and then create the Docker image in a separate step, which could be cumbersome and tricky. Let's see if multi-stage builds simplify the process.
Note: This requires Docker 17.05 or newer.
Let's start with a basic Go program:
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}Here's how we can build it using a single-stage Docker build with the golang:alpine image. The Dockerfile looks like this:
FROM golang:alpine
WORKDIR /app
ADD . /app
RUN cd /app && go build -o goapp
ENTRYPOINT ./goappBuild and run the Docker image:
docker build -t treeder/hello .
docker run --rm treeder/helloLet's check the image size with:
docker images | grep treeder/hello
The image is 258 MB, which is quite large for just a small Go binary. Now, let’s use a multi-stage build with this updated Dockerfile:
# Build stage
FROM golang:alpine AS build-env
RUN apk --no-cache add build-base git bzr mercurial gcc
ADD . /src
RUN cd /src && go build -o goapp
# Final stage
FROM alpine
WORKDIR /app
COPY --from=build-env /src/goapp /app/
ENTRYPOINT ./goappBuild and run this new Docker image:
docker build -t treeder/hello .
docker run --rm treeder/helloNow check the size:

It’s just 6.35 MB. Much more efficient!
In summary, multi-stage builds are highly effective and should be used nearly always to keep Docker images slim and optimized.


