Order matters: copy dependency files and install before copying
your source, so code changes don't bust the dependency cache
Multi-stage builds
Build in one stage, ship only the result in a tiny final stage
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN go build -o app
FROM gcr.io/distroless/base
COPY --from=build /src/app /app
USER nonroot
ENTRYPOINT ["/app"]
Final image has no compiler, no source — smaller and safer
Best practices
Small base images (-slim, alpine, distroless)
Run as a non-root user (USER)
Use a .dockerignore to keep junk out of the context
One concern per image; pin base image versions
Fewer, deliberate layers
Pushing to a registry
docker tag myapp:1.0 <user>/myapp:1.0
docker push <user>/myapp:1.0
Now teammates (and your cluster) can pull it
Use tags for versions; treat pushed images as immutable