# 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 ────────────────────────────────────────────────────────── # Uses Alpine (not distroless) so ffmpeg is available for on-demand voice # sample generation via pocket-tts (WAV→MP3 transcoding). FROM alpine:3.21 AS backend RUN apk add --no-cache ffmpeg ca-certificates && \ addgroup -S appgroup && adduser -S appuser -G appgroup COPY --from=builder /out/healthcheck /healthcheck COPY --from=builder /out/backend /backend USER appuser ENTRYPOINT ["/backend"] # ── runner service ─────────────────────────────────────────────────────────── # Uses Alpine (not distroless) so ffmpeg is available for WAV→MP3 transcoding # when pocket-tts voices are used. FROM alpine:3.21 AS runner RUN apk add --no-cache ffmpeg ca-certificates && \ addgroup -S appgroup && adduser -S appuser -G appgroup COPY --from=builder /out/healthcheck /healthcheck COPY --from=builder /out/runner /runner USER appuser ENTRYPOINT ["/runner"]