59 lines
2.1 KiB
Docker
59 lines
2.1 KiB
Docker
# syntax=docker/dockerfile:1
|
|
#
|
|
# Two build stages that do not know about each other — the Go binary and the
|
|
# Vite bundle — collapsed into one small runtime image. The server serves both
|
|
# the API and the built frontend, so there is a single process to run and a
|
|
# single port to expose.
|
|
|
|
# ------------------------------------------------------------------ web ----
|
|
FROM node:24-alpine AS web
|
|
RUN corepack enable
|
|
WORKDIR /web
|
|
COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./
|
|
RUN pnpm install --frozen-lockfile
|
|
COPY web/ ./
|
|
# The manifest this produces is what the Go server reads to find the hashed
|
|
# entry files, so a build without it would boot to an error.
|
|
RUN pnpm build
|
|
|
|
# ------------------------------------------------------------------ api ----
|
|
FROM golang:1.25-alpine AS api
|
|
WORKDIR /src
|
|
COPY api/go.mod api/go.sum ./
|
|
RUN go mod download
|
|
COPY api/ ./
|
|
# Static binary: the runtime image has no libc to link against.
|
|
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/server ./cmd/server
|
|
|
|
# -------------------------------------------------------------- runtime ----
|
|
FROM alpine:3.22 AS runner
|
|
# curl is here for the healthcheck; the binary itself needs nothing but certs.
|
|
RUN apk add --no-cache ca-certificates curl \
|
|
&& addgroup -S app -g 1001 && adduser -S app -u 1001
|
|
WORKDIR /app
|
|
|
|
COPY --from=api /out/server /usr/local/bin/
|
|
COPY --from=web --chown=app:app /web/dist ./web
|
|
|
|
ENV ADDR=:3000 WEB_DIR=/app/web
|
|
USER app
|
|
EXPOSE 3000
|
|
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD curl -fsS http://127.0.0.1:3000/api/health || exit 1
|
|
|
|
CMD ["server"]
|
|
|
|
# ---------------------------------------------------------------- seeder ----
|
|
# The fixture loader, for development and CI only. Its first act is to DELETE
|
|
# every campaign, organisation and user, so it must not be reachable from the
|
|
# image that runs in production: `--target runner` never builds these two
|
|
# stages, and nothing in compose references them.
|
|
FROM api AS seedbuild
|
|
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/seed ./test/seed
|
|
|
|
FROM runner AS seeder
|
|
COPY --from=seedbuild /out/seed /usr/local/bin/
|
|
USER app
|
|
CMD ["seed"]
|