Key idea
Same checklist again. Go compiles to one binary that needs nothing else to run, so the build stage has the whole Go toolchain and the runtime stage has only the binary. That makes the smallest image of the three.
Get the app
git clone https://github.com/computesphere-samples/learn.git
cd learn/labs/containerize-go
A small app with / and /healthz, using only Go's standard library. Already cloned it? Run cd ../containerize-go from another sample's folder.
.dockerignore
server
.env*
.git
Dockerfile
.dockerignore
server is the binary go build makes if you built it locally. The image compiles its own.
Dockerfile
# Stage 1: compile a static binary with the full Go toolchain.
FROM golang:1.27-alpine AS build
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/server .
# Stage 2: only the binary, on an image with no shell and no package manager.
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/server /server
ENV PORT=3000
EXPOSE 3000
USER nonroot:nonroot
ENTRYPOINT ["/server"]
What each choice does:
go.modis copied before the code, andgo mod downloadfetches the dependencies in their own cached layer. This app has none, but the habit pays off in the next one that does. If it also has ago.sum, copy that too.CGO_ENABLED=0guarantees a static binary that needs no C libraries, so it runs on an image that has none.-trimpath -ldflags "-s -w"leaves your folder paths and the debug symbols out of the binary, which makes it smaller.distroless/staticwith the:nonroottag contains little more than certificates and a non-root user.USER nonroot:nonrootsays so out loud (lesson 3.4.3).
Build, run and check
docker build -t containerize-go:1.0.0 .
docker run -d --rm --name containerize-go -p 3000:3000 containerize-go:1.0.0
curl -i localhost:3000/healthz
HTTP/1.1 200 OK
Content-Type: application/json
...
{"status":"ok"}
Then check the user and the size, and stop it:
docker image inspect containerize-go:1.0.0 --format '{{.Config.User}}'
docker image ls containerize-go
docker stop containerize-go
The user is nonroot:nonroot, and the image is about 15 MB. The golang image it was built with is hundreds of MB; none of that ships.
Check yourself