# @suaveplan/docker Docker Compose infrastructure for the genesis monorepo. Three stacks cover every stage of the development lifecycle. --- ## Stacks ### `docker-compose.dev.yml` — Development Persistent local services with named volumes. Ports are offset into the `8000` range so they do not collide with app servers running on the typical `3000`/`4000` range. Intended to run continuously while developing; data survives container restarts. ### `docker-compose.test.yml` — Integration Tests Ephemeral, no volumes. Ports are in the `7000` range, distinct from the dev stack so both can run simultaneously. Started automatically by CI and by the integration test suite's global setup; torn down after the run. ### `docker-compose.testbed.yml` — Testbed Staging Fully isolated on a private Docker network (`testbed-net`). Services communicate by service name only — no host ports are exposed for the backing services. Includes a `testbed` app container (built from `apps/testbed`) and a Caddy reverse proxy on host ports `3200`/`3201`. Used for full E2E staging runs before graduation. --- ## Port Table | Service | Dev host port | Test host port | Testbed | |---------|--------------|----------------|---------| | SurrealDB (WS) | 8001 | 7004 | internal only | | Redis | 8002 | 7005 | internal only | | PostgreSQL | 8003 | 7006 | internal only | | Mailpit SMTP | 8004 | 7007 | internal only | | Mailpit Web UI | 8005 | 7008 | internal only | | Typesense | 8006 | 7009 | internal only | | Garage S3 API | 8900 | 7010 | internal only | | Garage S3 Admin | 8902 | 7011 | internal only | | Caddy HTTP | — | — | 3200 | | Caddy HTTPS | — | — | 3201 | --- ## Starting and Stopping ### Development stack ```bash # From repo root bun run --filter=@suaveplan/docker dev:up bun run --filter=@suaveplan/docker dev:down # Or directly from docker/ docker compose -f docker-compose.dev.yml up -d docker compose -f docker-compose.dev.yml down ``` ### Integration-test stack ```bash bun run --filter=@suaveplan/docker test:up bun run --filter=@suaveplan/docker test:down # Or directly docker compose -f docker-compose.test.yml up -d docker compose -f docker-compose.test.yml down ``` ### Testbed staging stack ```bash bun run --filter=@suaveplan/docker testbed:up # builds apps/testbed first bun run --filter=@suaveplan/docker testbed:down # Or directly docker compose -f docker-compose.testbed.yml up --build -d docker compose -f docker-compose.testbed.yml down ``` --- ## Health Verification After starting a stack, verify individual services are healthy: ```bash # SurrealDB curl -sf http://localhost:8001/health && echo "surreal OK" # Redis redis-cli -p 8002 ping # expect PONG # PostgreSQL pg_isready -h localhost -p 8003 -U postgres # Mailpit Web UI curl -sf http://localhost:8005/api/v1/messages && echo "mailpit OK" # Typesense curl -sf "http://localhost:8006/health" -H "X-TYPESENSE-API-KEY: test" && echo "typesense OK" # Garage S3 curl -sf http://localhost:8900 && echo "garage OK" ``` For Docker-native health status: ```bash docker compose -f docker-compose.dev.yml ps ``` --- ## Environment Variable Bridge The env-bridge pattern lets you point test fixtures at services you are already running locally — for example, a managed SurrealDB instance — instead of starting a Docker container. A fixture **only** enters passthrough mode when **all** of its required variables are set and non-empty. If any variable in a group is missing or empty, the fixture spins up its own container. ### Setup ```bash cp docker/.env.example docker/.env # Edit docker/.env — fill in only the groups you want to bypass ``` The `.env` file is gitignored. Never commit real credentials. ### Variable Reference | Fixture | Required variables | |---------|--------------------| | SurrealDB | `SURREALDB_URL`, `SURREALDB_USERNAME`, `SURREALDB_PASSWORD` | | Redis | `REDIS_URL` | | PostgreSQL | `POSTGRES_URL` | | Mailpit | `MAILPIT_SMTP_URL`, `MAILPIT_API_URL` | | Typesense | `TYPESENSE_URL`, `TYPESENSE_API_KEY` | | Garage S3 | `GARAGE_S3_ENDPOINT`, `GARAGE_S3_ADMIN_ENDPOINT`, `GARAGE_S3_ACCESS_KEY_ID`, `GARAGE_S3_SECRET_ACCESS_KEY` | ### Default values (dev stack) | Variable | Default | |----------|---------| | `SURREALDB_URL` | `ws://localhost:8001` | | `SURREALDB_USERNAME` | `root` | | `SURREALDB_PASSWORD` | `root` | | `REDIS_URL` | `redis://localhost:8002` | | `POSTGRES_URL` | `postgresql://postgres:postgres@localhost:8003/postgres` | | `MAILPIT_SMTP_URL` | `smtp://localhost:8004` | | `MAILPIT_API_URL` | `http://localhost:8005` | | `TYPESENSE_URL` | `http://localhost:8006` | | `TYPESENSE_API_KEY` | `test` | | `GARAGE_S3_ENDPOINT` | `http://localhost:8900` | | `GARAGE_S3_ADMIN_ENDPOINT` | `http://localhost:8902` | --- ## Garage S3 — Key Retrieval After First Run Garage generates a root key on first startup. The access key ID and secret are **not** set via environment variables — you must retrieve them after the first run: ```bash # 1. Start the dev stack docker compose -f docker-compose.dev.yml up -d garage-s3 # 2. Create a layout (single-node, zone z1, 1 GB capacity) docker compose -f docker-compose.dev.yml exec garage-s3 \ /garage layout assign -z z1 -c 1G $( /garage node id 2>/dev/null | awk '{print $1}' ) docker compose -f docker-compose.dev.yml exec garage-s3 \ /garage layout apply --version 1 # 3. Create a key docker compose -f docker-compose.dev.yml exec garage-s3 \ /garage key create my-key # 4. Copy the key ID and secret into docker/.env GARAGE_S3_ACCESS_KEY_ID= GARAGE_S3_SECRET_ACCESS_KEY= ``` Refer to the [Garage documentation](https://garagehq.deuxfleurs.fr/documentation/quick-start/) for full setup instructions. --- ## Testbed Staging Environment The testbed stack builds `apps/testbed` locally and wires every service on the internal `testbed-net` network. Caddy proxies inbound traffic from `localhost:3200` to `testbed:3000`. ```bash # Build and start docker compose -f docker-compose.testbed.yml up --build -d # Follow testbed app logs docker compose -f docker-compose.testbed.yml logs -f testbed # Run Playwright E2E against the staging URL PLAYWRIGHT_BASE_URL=http://localhost:3200 bunx turbo test:e2e --filter=@suaveplan/testbed # Tear down docker compose -f docker-compose.testbed.yml down ``` The testbed service reads all connection strings from its `environment` block (defined in `docker-compose.testbed.yml`) and does not rely on `.env`. Service-to-service communication uses Docker DNS names (`surrealdb`, `redis`, etc.).