Files
ttrpg-initiative-tracker/docker-compose.yml
T
david raistrick 7467a8d30f feat(M5): docker-compose full stack (caddy + node backend + sqlite)
docker-compose.yml: two profiles.
  - backend: backend (node+ws+better-sqlite3, /data volume) + frontend
    (Caddy static build, STORAGE=ws, same-origin proxy)
  - firebase: existing Dockerfile + nginx (upstream path, untouched)
  Run: docker compose --profile backend up --build. OrbStack local now,
  remote docker context later.

server/Dockerfile: node:18-alpine, workspaces (shared dep), rebuild
better-sqlite3 for musl, DB at /data/tracker.sqlite.

Dockerfile.ws: CRA build STORAGE=ws → caddy:2-alpine serves /srv.
No backend URL baked (same-origin).

Caddyfile: handle /api/* + handle /ws → backend:4001 (path preserved,
mutually-exclusive handles so try_files SPA fallback never shadows proxy).
handle { static try_files } last. HTTP basic auth block optional.

src/storage/ws.js: same-origin defaults. Empty baseUrl = relative fetch
(Caddy proxy). wsUrl derives from window.location (http→ws/https→wss).
Fallback localhost for bare npm start dev.

.dockerignore: add data/ scratch/ tmp/ (never bake into image). Keep
Caddyfile in context (frontend build COPYs it).

Smoke verified via OrbStack:
  - GET / → 200 (static SPA)
  - PUT/GET /api/doc roundtrip → JSON persists
  - WS /ws subscribe + change push → both work through proxy

Firebase profile: pre-existing Dockerfile requires .env.local (hardcoded
COPY on main, not changed here). User must create file. Not a regression.
2026-07-01 14:39:47 -04:00

60 lines
1.6 KiB
YAML

# docker-compose.yml — two profiles:
# firebase: existing Dockerfile (nginx + firebase build), upstream path
# backend: full stack (caddy frontend + node backend + sqlite volume)
#
# Usage:
# docker compose --profile backend up --build # full self-hosted stack
# docker compose --profile firebase up --build # firebase-only (upstream)
#
# Run local in OrbStack; remote docker context later (just change context).
services:
# ---- full self-hosted stack (STORAGE=ws) ----
backend:
profiles: ["backend"]
build:
context: .
dockerfile: server/Dockerfile
image: ttrpg-backend:local
volumes:
- backend-data:/data
environment:
- DB_PATH=/data/tracker.sqlite
- PORT=4001
# - CORS_ORIGIN=* # Caddy same-origin, cors not strictly needed
expose:
- "4001"
restart: unless-stopped
frontend:
profiles: ["backend"]
build:
context: .
dockerfile: Dockerfile.ws
args:
- REACT_APP_TRACKER_APP_ID=${TRACKER_APP_ID:-ttrpg-initiative-tracker-default}
image: ttrpg-frontend:local
ports:
- "${FRONTEND_PORT:-8080}:80"
depends_on:
- backend
# Optional basic auth: set in .env as CADDY_BASIC_AUTH="user <hashed-pass>"
# Generate hash: caddy hash-password
environment:
- CADDY_BASIC_AUTH=${CADDY_BASIC_AUTH:-}
restart: unless-stopped
# ---- firebase-only path (upstream, existing Dockerfile) ----
firebase:
profiles: ["firebase"]
build:
context: .
dockerfile: Dockerfile
image: ttrpg-firebase:local
ports:
- "${FRONTEND_PORT:-8080}:80"
restart: unless-stopped
volumes:
backend-data: