feat(07-02): multi-stage Dockerfile for web + worker binaries
- Stage 1 (assets): downloads Tailwind v4.0.0 CLI, HTMX@2, Sortable.js 1.15.7; compiles minified CSS - Stage 2 (builder): runs templ generate @v0.3.1020; CGO_ENABLED=0 go build for /app/web and /app/worker - Stage 3 (runtime): gcr.io/distroless/static-debian12:nonroot; no CMD per D-08 - No .env files COPY'd into any layer (T-07-05 mitigated)
This commit is contained in:
parent
5550befffc
commit
f29bf0c765
1 changed files with 93 additions and 0 deletions
93
backend/Dockerfile
Normal file
93
backend/Dockerfile
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Multi-stage Dockerfile for the Xtablo Go backend.
|
||||
#
|
||||
# Three stages:
|
||||
# assets — downloads Tailwind CLI, HTMX, and Sortable.js then compiles the
|
||||
# Tailwind output CSS from tailwind.input.css + templates/. (D-09)
|
||||
# builder — copies assets, runs `templ generate`, and compiles both binaries
|
||||
# with CGO_ENABLED=0 so they are fully static. (D-07)
|
||||
# runtime — copies only the two binaries into a minimal distroless image.
|
||||
# No CMD is set — docker-compose overrides command: per service. (D-08)
|
||||
#
|
||||
# Build context: backend/ directory (sibling to go.mod).
|
||||
# From the repo root: docker build -f backend/Dockerfile backend/
|
||||
# From inside backend/: docker build .
|
||||
#
|
||||
# Security: no .env files are ever COPY'd into any layer (T-07-05).
|
||||
# The :nonroot distroless tag runs as uid 65532, preventing filesystem writes (T-07-07).
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 1: assets
|
||||
# Downloads pinned versions of Tailwind CLI, HTMX, and Sortable.js,
|
||||
# then compiles tailwind.input.css against templates/ for the minified CSS.
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM node:20-alpine AS assets
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
RUN mkdir -p static
|
||||
|
||||
# Tailwind standalone CLI — pinned at v4.0.0 (matches justfile tailwind_version)
|
||||
RUN curl -sSL -o /usr/local/bin/tailwindcss \
|
||||
"https://github.com/tailwindlabs/tailwindcss/releases/download/v4.0.0/tailwindcss-linux-x64" \
|
||||
&& chmod +x /usr/local/bin/tailwindcss
|
||||
|
||||
# HTMX — pinned at major version 2 (matches justfile htmx_version)
|
||||
RUN curl -sSL -o static/htmx.min.js \
|
||||
"https://unpkg.com/htmx.org@2/dist/htmx.min.js"
|
||||
|
||||
# Sortable.js — pinned at v1.15.7 (matches justfile sortable_version)
|
||||
RUN curl -sSL -o static/sortable.min.js \
|
||||
"https://cdn.jsdelivr.net/npm/sortablejs@1.15.7/Sortable.min.js"
|
||||
|
||||
# Copy Tailwind input and templates for content-scanning
|
||||
COPY tailwind.input.css .
|
||||
COPY templates/ templates/
|
||||
|
||||
# Compile and minify Tailwind CSS
|
||||
RUN tailwindcss -i tailwind.input.css -o static/tailwind.css --minify
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 2: builder
|
||||
# Compiles both Go binaries (cmd/web and cmd/worker) with CGO_ENABLED=0.
|
||||
# Runs `templ generate` first since *_templ.go files are gitignored. (D-07)
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM golang:1.26-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Download dependencies first (layer-cached until go.mod/go.sum change)
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy the entire backend context
|
||||
COPY . .
|
||||
|
||||
# Overwrite static/ with the freshly built assets from the assets stage
|
||||
COPY --from=assets /build/static ./static
|
||||
|
||||
# Install templ at the pinned version and run template generation
|
||||
# (gitignored *_templ.go files must be generated at build time)
|
||||
RUN go install github.com/a-h/templ/cmd/templ@v0.3.1020 && templ generate
|
||||
|
||||
# Compile the web server binary — CGO_ENABLED=0 required for distroless/static
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -trimpath -o /app/web ./cmd/web
|
||||
|
||||
# Compile the background worker binary — CGO_ENABLED=0 required for distroless/static
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -trimpath -o /app/worker ./cmd/worker
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 3: runtime
|
||||
# Minimal distroless image containing only the two compiled binaries.
|
||||
# Runs as nonroot (uid 65532) — no filesystem write access. (T-07-07)
|
||||
# No CMD set — docker-compose.prod.yaml provides `command:` per service. (D-08)
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
|
||||
COPY --from=builder /app/web /app/web
|
||||
COPY --from=builder /app/worker /app/worker
|
||||
|
||||
EXPOSE 8080
|
||||
Loading…
Reference in a new issue