Files
go-simple-api/Dockerfile
T
2026-07-16 10:13:46 +03:30

51 lines
1.9 KiB
Docker

# ---- Stage 1: build ----
# This stage has the full Go toolchain and is only used to compile the
# binary. It is discarded entirely after the build - none of its ~800MB+
# footprint ends up in the final image.
FROM golang:1.26 AS builder
WORKDIR /app
# Copy just the module files first and download dependencies before
# copying the rest of the source. Docker caches each instruction as a
# layer; as long as go.mod/go.sum don't change, this layer (and the
# downloads it triggers) is reused on every subsequent build, even if
# application code changes constantly. If we copied all source first, any
# code edit would invalidate this cache and force a full re-download every
# single build.
COPY go.mod go.sum* ./
RUN go mod download
COPY . .
# CGO_ENABLED=0 produces a fully static binary with no dynamic library
# dependencies, which is what lets it run on the minimal Alpine base image
# in stage 2 below without missing shared libraries. GOOS=linux ensures we
# cross-compile for Linux even if you're building this image on macOS or
# Windows.
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/bin/server ./cmd/api
# ---- Stage 2: run ----
# A tiny (~7MB) base image that receives ONLY the compiled binary from
# stage 1 - no compiler, no source code, no build tools. Smaller image,
# smaller attack surface.
FROM alpine:3.20
# Alpine's minimal base doesn't include root CA certificates by default.
# Without these, any outbound HTTPS call this app makes (Google's OAuth2
# token exchange and userinfo endpoints) would fail with a certificate
# verification error. --no-cache avoids leaving package-manager cache
# files behind in the image.
RUN apk add --no-cache ca-certificates
WORKDIR /app
# Pull just the compiled binary out of the builder stage - this is the
# actual multi-stage mechanism: --from=builder reaches back into the FIRST
# image just for this one file.
COPY --from=builder /app/bin/server .
EXPOSE 8080
CMD ["./server"]