Go cookbook · Beginner
Build a small, secure container image for a Go service
Go images are large, run as root and include build tools.
Approach
Build a static binary in a builder stage with module and build caches, then copy it into a distroless (or scratch) image that runs as a non-root user.
Solution
# syntax=docker/dockerfile:1
FROM golang:1.27 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/server ./cmd/server
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
Pitfalls
CGO_ENABLED=1(the default when a C toolchain exists) producing a binary that needs glibc in the runtime image.- Copying the whole repository before
go mod download, defeating layer caching. - Scratch images without CA certificates or timezone data when the service needs them (distroless includes them).
Variations
- Embed version info with
-ldflags "-X main.version=$VERSION". - Build multi-arch images with
docker buildx build --platform linux/amd64,linux/arm64usingGOOS/GOARCH.
Further reading: Docker Go guide
Reference: https://docs.docker.com/guides/golang/
More Go recipes
- Benchmark code with testing.B.Loop
- Decode and validate JSON request bodies safely
- Embed static files and templates in the binary
- Load and validate configuration from environment variables
- Log structured JSON with slog and redact secrets
- Process jobs concurrently with a bounded worker pool
- Profile a running service with pprof
- Rate limit requests per client