Add Compose stack & Caddy deploy artifacts (task 05)

Multi-stage Dockerfile (pnpm fetch/offline, standalone output, non-root
nextjs user), deploy/compose.yaml with famapp + famapp-db + full Authentik
stack on famapp_net, Caddyfile.snippet for fam/auth.ginnoir.com, and
.env.production.example. Added .dockerignore and public/.gitkeep.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ginnoir
2026-05-06 02:23:28 -05:00
co-authored by Claude Sonnet 4.6
parent e00c95d4d6
commit f59753404e
6 changed files with 99 additions and 1 deletions
+10
View File
@@ -0,0 +1,10 @@
node_modules
.next
.git
deploy
drizzle
docs
*.md
.env*
!.env.example
.vscode
+40
View File
@@ -0,0 +1,40 @@
# ── famapp ────────────────────────────────────────────────────────────────────
# Public URL for the app (used in share links, OIDC redirect URIs, etc.)
NEXT_PUBLIC_APP_URL=https://fam.ginnoir.com
# famapp Postgres credentials (used to build DATABASE_URL inside compose.yaml)
FAMAPP_DB_USER=famapp
FAMAPP_DB_PASSWORD=replace-with-strong-password
FAMAPP_DB_NAME=famapp
# Auth.js session secret — generate with: openssl rand -base64 32
AUTH_SECRET=replace-with-openssl-rand-base64-32
# OIDC provider (Authentik) — task 06 will fill these in after bootstrapping
AUTH_OIDC_ISSUER=https://auth.ginnoir.com/application/o/famapp/
AUTH_OIDC_CLIENT_ID=replace-me
AUTH_OIDC_CLIENT_SECRET=replace-me
# Web Push VAPID keys — generate with: pnpm vapid:generate
VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=
# Must be "mailto:<address>" or a URL
VAPID_SUBJECT=mailto:you@example.com
# ntfy (optional push fallback — leave blank to disable)
NTFY_URL=
NTFY_TOPIC=
# Log level: error | warn | info | debug
LOG_LEVEL=info
# ── Authentik ─────────────────────────────────────────────────────────────────
# Authentik Postgres credentials (separate DB per Matt's rule)
AUTHENTIK_DB_USER=authentik
AUTHENTIK_DB_PASSWORD=replace-with-strong-password
AUTHENTIK_DB_NAME=authentik
# Authentik secret key — generate with: openssl rand -base64 60
AUTHENTIK_SECRET_KEY=replace-with-openssl-rand-base64-60
+37
View File
@@ -0,0 +1,37 @@
# syntax=docker/dockerfile:1
# ── Stage 1: fetch dependencies ───────────────────────────────────────────────
FROM node:22-alpine AS deps
RUN corepack enable && corepack prepare pnpm@10.33.3 --activate
WORKDIR /app
COPY package.json pnpm-lock.yaml .npmrc ./
# Populate the pnpm virtual store; this layer is cached until lock file changes.
RUN pnpm fetch
# ── Stage 2: build ────────────────────────────────────────────────────────────
FROM deps AS builder
WORKDIR /app
# CI=true prevents pnpm from prompting for TTY confirmation when removing modules dir
ENV CI=true
ENV NEXT_TELEMETRY_DISABLED=1
COPY . .
RUN pnpm install --offline --frozen-lockfile
RUN pnpm build
# ── Stage 3: production runtime ───────────────────────────────────────────────
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
+3 -1
View File
@@ -9,9 +9,11 @@ Living progress tracker. Update at the end of each task. The canonical brief is
- **03 — Drizzle + Postgres setup**. drizzle-orm + postgres driver + drizzle-kit wired up. `src/modules/_core/schema.ts` declares `users`, `households`, `household_members`. `docker-compose.dev.yaml` starts Postgres 16. `drizzle/0000_silent_magma.sql` generated and applied. `tsc --noEmit` passes.
- **04 — Module loader & registry**. `src/modules/_core/module.ts` types (`ModuleManifest`, `EntityTypeRegistration`, `DashboardWidget`, etc.), `registry.ts` singleton with `registerModule`/`getRegistry`/`getEntityType`/`getWidget`, barrel `_core/index.ts`. Stub manifests for `calendar`, `lists`, `notes`. `src/modules/index.ts` loader. Root layout imports loader; `AppNav` reads registry for nav links. `/debug/registry` dumps full registry JSON (dev only). Uses zod v4 + built-in `z.toJSONSchema()`. `tsc --noEmit`, `pnpm build`, `pnpm lint` all clean.
- **05 — Compose + Caddy**. `Dockerfile` (3-stage: deps/builder/runner, pnpm fetch + offline install, non-root `nextjs` user, `output: standalone`), `deploy/compose.yaml` (famapp + famapp-db + full Authentik stack on `famapp_net`), `deploy/Caddyfile.snippet`, `.env.production.example`. `docker build -t famapp .` succeeds (~311 MB); `docker compose -f deploy/compose.yaml config` validates clean. Added `.dockerignore` and `public/.gitkeep`.
## Next up
- **05Compose + Caddy** ([brief](docs/tasks/05-compose-caddy.md)).
- **06Authentik OIDC** ([brief](docs/tasks/06-authentik-oidc.md)).
## Phase 1 remaining
+9
View File
@@ -0,0 +1,9 @@
# Include from main Caddyfile or paste into the existing one.
fam.ginnoir.com {
reverse_proxy famapp:3000
}
auth.ginnoir.com {
reverse_proxy authentik-server:9000
}
View File