5.5 KiB
Docker Compose deployment/runbook
Requested configuration
.env includes exactly:
HOST_PORT=8155
PUBLIC_DOMAIN=http://localhost
The actual browser URL is http://localhost:8155. PUBLIC_DOMAIN is the domain origin requested by owner and deliberately has no port. For local URL construction derive PUBLIC_BASE_URL=http://localhost:8155; prefer relative API URLs (/api/v1/...) on the web client. Do not send client requests directly to container name api (not browser-resolvable).
M0 required deliverables
apps/web/Dockerfile: multi-stage Node build → lightweight nginx runtime, serving Vite output and forwarding/api/,/healthtoapi:3000. Create nginx config underapps/web/nginx.conf. Use SPA history fallback toindex.html; ensure/healthand/api/proxy rules take precedence.apps/server/Dockerfile: multi-stage Node build, Node runtime listening on0.0.0.0:3000and health endpoint.compose.yamlis provided; adapt only if necessary for a working, secure Compose setup, preserving exposed host port, private DB/API, healthcheck and volume. If Node native dependencies require extra Docker libraries, document why.- Use
NODE_ENV=developmentonly for initial local setup; deployment builds should run production assets and API with runtime environment. - Implement Drizzle migrations on container initialization as a controlled documented command, not an uncontrolled migration loop. Provide
npm run db:migrate,npm run db:seed. docker compose configshould parse correctly before Docker build.
Commands
# local bootstrap (after M0 files created)
docker compose config
docker compose up --build -d
docker compose ps
curl -fsS http://localhost:8155/health
# Stop (keep database)
docker compose down
# NEVER use -v unless intentionally deleting data
docker compose down -v
Critical security note
The starter .env has a deliberately weak local-only placeholder password. Generate a strong password before exposing the app or sharing .env. .env is gitignored. In this MVP there is no user authentication: do not open the app to the public internet until auth/access control and API throttling are implemented.
Future reverse proxy
Later replace PUBLIC_DOMAIN with a true HTTPS origin and configure TLS at the reverse proxy; no fixed 8155 should be appended for an external HTTPS origin. Keep Compose HOST_PORT configurable; enable trusted proxy headers only for known proxy. Do not overbuild this now.
Implemented M0 bootstrap
Compose publishes web on loopback (127.0.0.1:${HOST_PORT}:8080), with nginx running as an unprivileged user. API and DB have no published ports. The bootstrap one-shot service uses the API image to run node dist/migrate.js then node dist/seed.js; API waits for successful completion. A PostgreSQL transaction advisory lock serializes migration execution. A migration tracking table prevents repeat schema application. Player seeds use conflict-safe inserts and preserve existing records. The M1 catalog seed synchronizes the current versioned definition; it does not rewrite plant genomes or ancestry.
For explicit reruns use docker compose run --rm bootstrap node dist/migrate.js or docker compose run --rm bootstrap node dist/seed.js. Runtime images contain compiled JavaScript; root npm migration/seed scripts use TypeScript tooling for host development with an explicit DATABASE_URL. Compose production mode overrides the starter development value without modifying .env.
M3–M4 persistence and collection
The additive 0002_plant_details migration adds plant names, creation source and persisted birth rarity reasons. Migrations are applied in filename order inside a single advisory-locked transaction and tracked by name; repeat bootstrap does not rewrite existing plants, their parents, generations, genomes or seeds. The fixed demo owner is only sandbox scoping and must not be treated as real authentication.
The seed now creates exactly 100 fixed-ID demonstration specimens, including the original two named parents. Conflicting plant IDs are left untouched. A fresh installation therefore has 100 demo plants; manual designs and bred offspring increase the total. Curated demo modifiers are labeled as demonstrations rather than bred mutation outcomes.
Run the isolated acceptance database with npm run test:integration, then docker compose -f compose.test.yaml down. That Compose project uses a tmpfs database, no persistent volume and no published ports. It neither resets nor connects to the application's named volume. The deliberate rollback test logs an expected PostgreSQL exception while checking that the API returns only a safe generic response.
API examples and cursor/idempotency contracts are documented in docs/API_EXAMPLES.md. Before any public deployment add real identity, owner authorization, rate limits, TLS/access controls and operational backup/restore checks. The original milestone deployment used loopback-only publication; see the LAN tunnel configuration below for the current binding. Future game scope is documented in docs/ROADMAP.md and is not implemented.
LAN tunnel configuration — 2026-10-28
The owner configured botanica.p20.dev to use origin http://192.168.1.197:8155. Web publication therefore uses ${HOST_BIND:-0.0.0.0}:${HOST_PORT:-8155}:8080 so the tunnel can reach the host over LAN. HOST_BIND can be set to 127.0.0.1 for loopback-only access. API and PostgreSQL remain unpublished. HOST_PORT=8155 and PUBLIC_DOMAIN=http://localhost in .env were preserved; PUBLIC_DOMAIN is not a hostname allowlist.