# syntax=docker/dockerfile:1 # --- Build stage ------------------------------------------------------- # golang:1.26 matches the "go 1.26.0" directive in go.mod (GOTOOLCHAIN=auto # would otherwise re-download the right toolchain anyway, but pinning here # keeps builds hermetic and fast). FROM golang:1.26-alpine AS builder WORKDIR /src # Cache deps in their own layer. COPY go.mod go.sum ./ RUN --mount=type=cache,target=/go/pkg/mod \ go mod download COPY . . # CGO disabled -> static binary, runs on the scratch-ish alpine base below # with no libc surprises. Both entrypoints are built from the same module: # cmd/api is the long-running server, cmd/seed is the one-shot admin # bootstrap job (see charts/backend/templates/seed-job.yaml). RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/api ./cmd/api && \ CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/seed ./cmd/seed # --- Runtime stage ------------------------------------------------------- FROM alpine:3.20 RUN apk add --no-cache ca-certificates tzdata && \ addgroup -S app && adduser -S app -G app WORKDIR /app COPY --from=builder /out/api /out/seed ./ # Migrations are baked into the image so the migration Job (see # charts/backend/templates/migration-job.yaml) always runs the set that # matches the running server, without needing a separate artifact. COPY --from=builder /src/migrations ./migrations # Local-disk media storage (MEDIA_STORAGE_DRIVER=local) writes here; the # chart mounts a PVC at this path when persistence is enabled. Owned by the # non-root "app" user created above. RUN mkdir -p /app/uploads /app/verification-uploads && \ chown -R app:app /app USER app EXPOSE 8080 ENTRYPOINT ["/app/api"]