Use a multi-stage Docker build to download Go modules in a builder stage and copy only the binary to the final image for optimal caching.
You cannot directly cache the Go module download cache (GOMODCACHE) inside a Docker build layer because the go command invalidates it based on the go.mod file hash, and Docker layers are immutable. Instead, you must use a multi-stage build to download dependencies in a builder stage and copy only the compiled binary to the final image. This approach ensures the final image is small and does not include the module cache.
FROM golang:1.23 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .
FROM alpine:latest
COPY --from=builder /app/main /main
CMD ["/main"]
Docker builds are fast when they reuse cached layers, but the Go module cache is tricky because it changes whenever your dependency list changes. If you try to cache it directly, your builds will often fail or become inconsistent. The solution is to separate the 'downloading' step from the 'building' step. You download all dependencies first, then copy your code, and finally build. This way, if only your code changes, Docker reuses the dependency download layer, making your builds much faster.