# syntax=docker/dockerfile:1 FROM golang:1.26.1-alpine AS builder WORKDIR /app # Download modules into the BuildKit cache so they survive across builds. # This layer is only invalidated when go.mod or go.sum changes. COPY go.mod go.sum ./ RUN --mount=type=cache,target=/root/go/pkg/mod \ go mod download COPY . . ARG VERSION=dev ARG COMMIT=unknown # Build all three binaries in a single layer so the Go compiler can reuse # intermediate object files. Both cache mounts are preserved between builds: # /root/go/pkg/mod — downloaded module source # /root/.cache/go-build — compiled package objects (incremental recompile) RUN --mount=type=cache,target=/root/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 GOOS=linux go build \ -ldflags="-s -w -X main.version=${VERSION} -X main.commit=${COMMIT}" \ -o /out/backend ./cmd/backend && \ CGO_ENABLED=0 GOOS=linux go build \ -ldflags="-s -w -X main.version=${VERSION} -X main.commit=${COMMIT}" \ -o /out/runner ./cmd/runner && \ CGO_ENABLED=0 GOOS=linux go build \ -ldflags="-s -w" \ -o /out/healthcheck ./cmd/healthcheck # ── backend service ────────────────────────────────────────────────────────── FROM gcr.io/distroless/static:nonroot AS backend COPY --from=builder /out/healthcheck /healthcheck COPY --from=builder /out/backend /backend ENTRYPOINT ["/backend"] # ── runner service ─────────────────────────────────────────────────────────── FROM gcr.io/distroless/static:nonroot AS runner COPY --from=builder /out/healthcheck /healthcheck COPY --from=builder /out/runner /runner ENTRYPOINT ["/runner"]