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
).
# 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"]
# 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"]
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.