Reducing Docker image size by using multi-stage builds

By leveraging the multi-stage build capabilities introduced in Docker 17.05, it’s possible to compile a binary on a full image and then move the resulting binary onto an empty image (FROM scratch).

Without modules (no vendor)

# Build the go application into a binary
FROM golang:alpine AS builder
WORKDIR /app
ADD . ./
RUN apk update && apk add git
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Run the binary on an empty container
FROM scratch
COPY --from=builder /app/main .
ENTRYPOINT ["/main"]

With modules (vendor)

# Build the go application into a binary
FROM golang:alpine AS builder
WORKDIR /app
ADD . ./
RUN CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -installsuffix cgo -o main .

# Run the binary on an empty container
FROM scratch
COPY --from=builder /app/main .
ENTRYPOINT ["/main"]

Tips and Tricks

Another useful piece of information you may need is, if your image is making external calls, it may need CA certificates.

To include these in your minimal image, you need to add the following to your builder:

RUN apk --update add ca-certificates

And then to copy them to your minimal image:

COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt

With vendoring enabled, it would look like this:

# Build the go application into a binary
FROM golang:alpine AS builder
WORKDIR /app
ADD . ./
RUN CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -installsuffix cgo -o main .
RUN apk --update add ca-certificates

# Run the binary on an empty container
FROM scratch
COPY --from=builder /app/main .
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ENTRYPOINT ["/main"]

For reference, under-maintenance is a project I made that uses multi-stage build and the resulting image is less than 7MB.