docs(07-02): complete plan summary — Dockerfile and .env.example S3/R2 vars
This commit is contained in:
parent
0781403f5c
commit
2329e19e75
1 changed files with 104 additions and 0 deletions
104
.planning/phases/07-deploy-v1/07-02-SUMMARY.md
Normal file
104
.planning/phases/07-deploy-v1/07-02-SUMMARY.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
---
|
||||
phase: 07-deploy-v1
|
||||
plan: "02"
|
||||
subsystem: backend-deploy
|
||||
tags: [go, docker, dockerfile, multi-stage, distroless, s3, env]
|
||||
dependency_graph:
|
||||
requires:
|
||||
- 07-01 (embed.FS anchor + RunMigrations — both binaries reference backend package)
|
||||
provides:
|
||||
- backend/Dockerfile — 3-stage multi-stage Docker build producing /app/web and /app/worker (DEPLOY-01)
|
||||
- backend/.env.example — complete operator reference for S3/R2 and production vars (DEPLOY-02)
|
||||
affects:
|
||||
- backend/Dockerfile (new file)
|
||||
- backend/.env.example (updated)
|
||||
tech_stack:
|
||||
added:
|
||||
- gcr.io/distroless/static-debian12:nonroot — minimal runtime base image (D-07)
|
||||
- node:20-alpine — asset compilation stage (Tailwind standalone CLI v4.0.0)
|
||||
- golang:1.26-alpine — Go compilation stage
|
||||
patterns:
|
||||
- Multi-stage Docker build: assets → builder → runtime
|
||||
- No CMD in final stage — docker-compose overrides command: per service (D-08)
|
||||
- CGO_ENABLED=0 static binaries for distroless compatibility (D-07)
|
||||
- templ generate at pinned v0.3.1020 before go build (gitignored *_templ.go files)
|
||||
- go build cache mount (--mount=type=cache,target=/root/.cache/go-build) for layer caching
|
||||
key_files:
|
||||
created:
|
||||
- backend/Dockerfile
|
||||
modified:
|
||||
- backend/.env.example
|
||||
decisions:
|
||||
- "node:20-alpine for assets stage — matches Node version used by JS monorepo apps/api/Dockerfile"
|
||||
- "Tailwind downloaded as standalone binary (not npm package) — matches justfile bootstrap approach"
|
||||
- "Both go build commands use separate RUN --mount=type=cache,target=/root/.cache/go-build — enables independent layer caching per binary"
|
||||
- "DOMAIN var commented out in .env.example — it is only needed in production .env.prod and should not have a misleading default value"
|
||||
- "S3_USE_PATH_STYLE=true as dev default — MinIO requires path-style; prod R2 operator must flip to false"
|
||||
metrics:
|
||||
duration: "~4 minutes"
|
||||
completed: "2026-05-15"
|
||||
tasks: 2
|
||||
files: 2
|
||||
---
|
||||
|
||||
# Phase 7 Plan 2: Multi-stage Dockerfile and .env.example S3/R2 vars Summary
|
||||
|
||||
## What Was Built
|
||||
|
||||
3-stage multi-stage Dockerfile producing two CGO_ENABLED=0 static binaries (/app/web and /app/worker) in a distroless nonroot runtime image, plus .env.example updated with S3/R2 vars, MAX_UPLOAD_SIZE_MB, and a commented DOMAIN entry for production operators.
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commit | Files |
|
||||
|------|------|--------|-------|
|
||||
| 1 | Multi-stage Dockerfile for web + worker binaries | f29bf0c | backend/Dockerfile |
|
||||
| 2 | Update .env.example with S3/R2, DOMAIN, MAX_UPLOAD_SIZE_MB vars | 0781403 | backend/.env.example |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
1. The assets stage uses `node:20-alpine` (not pure alpine + curl) to avoid custom tool installation — the Tailwind standalone CLI is a binary download anyway, so any alpine works; node:20 matches the JS monorepo API Dockerfile for consistency.
|
||||
|
||||
2. Both `go build` RUN instructions use separate `--mount=type=cache` directives so Docker can cache each binary's compilation independently. If only `cmd/web` changes, the `cmd/worker` layer reuses the cache.
|
||||
|
||||
3. `DOMAIN` is commented out in `.env.example` rather than given a placeholder value. A live domain value with no comment context could mislead operators into thinking the empty string is a valid default; the comment makes the production-only context explicit.
|
||||
|
||||
4. `S3_USE_PATH_STYLE=true` is the dev default (MinIO requires path-style URLs). The comment explicitly instructs production operators to set this to `false` for Cloudflare R2 virtual-hosted-style URLs.
|
||||
|
||||
5. `TEST_DATABASE_URL` comment updated to flag it as dev/test only — the .env.example serves as the reference for `.env.prod` and the clarification prevents accidentally including test connection strings in production.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Verification
|
||||
|
||||
Dockerfile structure verified by inspecting key markers:
|
||||
- 3 named stages: `assets`, `builder`, runtime (from `gcr.io/distroless/static-debian12:nonroot`)
|
||||
- `templ generate` at `v0.3.1020`
|
||||
- `CGO_ENABLED=0 GOOS=linux` on both go build commands
|
||||
- No `CMD` instruction in final stage
|
||||
- No `COPY .env*` anywhere (T-07-05 mitigated)
|
||||
|
||||
.env.example verified:
|
||||
```
|
||||
grep count for S3_ENDPOINT|S3_BUCKET|S3_ACCESS_KEY|S3_SECRET_KEY|S3_REGION|S3_USE_PATH_STYLE|MAX_UPLOAD_SIZE_MB|DOMAIN = 8
|
||||
```
|
||||
All 8 new vars present. All original vars (DATABASE_URL, TEST_DATABASE_URL, SESSION_SECRET, PORT, ENV) preserved.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
No new threat surface beyond plan's threat_model.
|
||||
|
||||
- T-07-05 mitigated: No `.env*` files are COPY'd in any Dockerfile stage. Secrets reach the runtime only via `env_file:` in docker-compose at runtime.
|
||||
- T-07-07 mitigated: `:nonroot` tag confirmed in final FROM line — runs as uid 65532.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- backend/Dockerfile: EXISTS
|
||||
- backend/.env.example: EXISTS (contains S3_ENDPOINT, S3_BUCKET, S3_REGION, S3_ACCESS_KEY, S3_SECRET_KEY, S3_USE_PATH_STYLE, MAX_UPLOAD_SIZE_MB, DOMAIN)
|
||||
- Commit f29bf0c: VERIFIED in git log
|
||||
- Commit 0781403: VERIFIED in git log
|
||||
Loading…
Reference in a new issue