6.4 KiB
@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
# 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
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
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:
# 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:
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
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:
# 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=<key-id>
GARAGE_S3_SECRET_ACCESS_KEY=<secret>
Refer to the Garage documentation 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.
# 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.).