Docker image size matters. Weblog post lengths matter as well. So here’s a Dockerfile:
# Stage 1
# Use the 800+MB official golang image as build container
FROM golang:1.14 AS builder
# Copy our app
COPY . .
# Build our app
RUN go build
# Stage 2
# Start a new container based on the small buster-slim image
# Copy app executable from builder container
COPY --from=builder /go/src/app/my-app .
# Copy CA certificates to prevent x509: certificate signed by unknown authority errors
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
# Configure the container to start our app
Stick this in a
Dockerfile in the root of your project and build:
# Build it
docker build -t my-app .
# See the image size
docker image ls my-app
# Run it
docker run -it --rm my-app
This multi-stage build results in an image size 80MB plus the size of your application binary. Mind you, this is a mere starting point. You’ll want to consider running the app as non-root, among other things.
There are ways to create even smaller images:
Base it on alpine; it will save you a few megabytes (but not more!) but at the cost of potential incompatibilities. I use debian-based images all through my workflow so switching to Alpine was not worth it for me.
FROM scratch; make a container with only your binary. Thanks to Go’s static linking it is possible to do this, but in my experience you’ll miss the plumbing an actual base image provides eventually. Docker itself states that scratch is most useful in the context of building base images (such as debian and busybox).
debian:slim provides a nice middle ground between the 800M+ golang docker image and starting with absolutely nothing.