- pnpm 10 workspace + TypeScript strict + ESLint flat + Prettier - CLAUDE.md as canonical brief - docs/tasks/ — 22 task briefs broken out by phase for sub-sessions - docs/decisions/ — ADR scaffold Implements task 01 (repo-init).
1.9 KiB
1.9 KiB
05 — Compose stack & Caddy
Goal
Produce the production deploy artifacts: Dockerfile, deploy/compose.yaml, Caddy snippet, .env.production.example.
Depends on
- 02 (so
output: "standalone"exists) - 03 (so DB service is defined)
Scope
Dockerfile (multi-stage)
- Stage 1:
node:22-alpine+ pnpm, install deps, build (pnpm build). - Stage 2:
node:22-alpineruntime, copy.next/standalone,.next/static,public/. Run as non-root.CMD ["node", "server.js"].
deploy/compose.yaml
Services:
famapp(built from Dockerfile)famapp-db(postgres:16, volumefamapp_db_data)authentik-server,authentik-worker,authentik-db(postgres:16, volumeauthentik_db_data),authentik-redis— leave fully configured but task 06 will tune env
All services on a famapp_net network. famapp depends on famapp-db. No host port exposure for the DBs/redis.
Caddy snippet (deploy/Caddyfile.snippet)
fam.ginnoir.com {
reverse_proxy famapp:3000
}
auth.ginnoir.com {
reverse_proxy authentik-server:9000
}
Comment at the top: "Include from main Caddyfile or paste into the existing one."
.env.production.example
All vars needed by the compose stack, with comments explaining each.
Out of scope
- Actually running the stack against ginnoir.com (Matt does that).
- Backups, log shipping (later tasks).
Acceptance criteria
docker build -t famapp .succeeds.docker compose -f deploy/compose.yaml configvalidates without errors.- famapp container starts against famapp-db when given a populated
.env. - Image size under ~300 MB.
- No secrets committed; only
.env.production.example.
Notes
- Use
pnpm fetch+pnpm install --offlinein the build stage for cache locality. output: "standalone"(set in task 02) means we copy.next/standalone/server.js, not runnext start.