feat(dev): local docker-compose stack
ci / shared (pull_request) Successful in 4s
ci / validate (pull_request) Successful in 2s

Adds dev/docker-compose.yml + dev/keycloak/realm-export.json + dev/README.md
and Makefile targets so a developer can:

  make dev-up

and get Keycloak 26 on :8080 with the breakpilot-dev realm pre-imported,
plus pg-app (:5432), Redis (:6379), Mongo (:27017), and MinIO (:9000 + :9001).

Seed users:
  test@breakpilot.dev / test    — IT_ADMIN of tenant 'acme'
  admin@breakpilot.dev / admin  — BREAKPILOT_ADMIN (platform staff)

Realm includes a dev-portal public PKCE client (redirect URIs cover
http://localhost:3000/* and http://*.localhost:3000/* so subdomain routing
works in dev) and a dev-tenant-registry bearer-only client. Protocol
mappers project tenant_id, tenant_slug, org_roles, products, plan, and
tenant_status into every issued JWT — the contract portal + tenant-registry
expect in prod, fronted by Keycloak attributes today.

dev/ lives in orca-platform because this repo already documents the
production topology that this compose mirrors. INFRASTRUCTURE.md §1 sets
dev as 'docker-compose on developer laptops' — this is that compose.

Refs: M0.1+ (precondition for local-dev work on tenant-registry / portal)
This commit is contained in:
2026-05-18 22:37:35 +02:00
parent 8e37f65b8e
commit 2a807d7671
6 changed files with 444 additions and 1 deletions
+139
View File
@@ -0,0 +1,139 @@
# Local-dev stack for the Breakpilot Platform.
#
# Brings up the dependencies a developer needs to run tenant-registry +
# portal against a real Keycloak realm with seed data — no SysEleven VMs,
# no PowerDNS, no Stalwart. Loopback only.
#
# make dev-up bring this stack up
# make dev-down stop it, keep volumes
# make dev-reset stop, wipe volumes, bring up fresh
#
# Networking: every service joins the `breakpilot-dev` bridge network so
# tenant-registry and portal (running on the host, not in this compose)
# can reach Postgres etc. via `localhost:<port>`. Inter-service traffic
# inside the compose (Keycloak → pg-keycloak) uses the service name.
name: breakpilot-dev
networks:
breakpilot-dev:
driver: bridge
volumes:
pg-keycloak-data:
pg-app-data:
mongo-data:
minio-data:
services:
# ─── Identity ──────────────────────────────────────────────────────────
pg-keycloak:
image: postgres:16-alpine
restart: unless-stopped
networks: [breakpilot-dev]
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: keycloak-dev-pass
volumes:
- pg-keycloak-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
interval: 5s
timeout: 3s
retries: 10
keycloak:
image: quay.io/keycloak/keycloak:26.0
restart: unless-stopped
depends_on:
pg-keycloak:
condition: service_healthy
networks: [breakpilot-dev]
ports:
- "8080:8080"
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://pg-keycloak:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak-dev-pass
KC_HEALTH_ENABLED: "true"
KC_HOSTNAME: localhost
KC_HTTP_ENABLED: "true"
KC_PROXY_HEADERS: xforwarded
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin-dev-pass
command:
- start-dev
- --import-realm
volumes:
- ./keycloak/realm-export.json:/opt/keycloak/data/import/breakpilot-dev.json:ro
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n' >&3 && grep -q UP <&3"]
interval: 10s
timeout: 5s
retries: 20
start_period: 30s
# ─── App data ──────────────────────────────────────────────────────────
pg-app:
image: postgres:16-alpine
restart: unless-stopped
networks: [breakpilot-dev]
ports:
- "5432:5432"
environment:
POSTGRES_DB: platform
POSTGRES_USER: platform
POSTGRES_PASSWORD: platform-dev-pass
volumes:
- pg-app-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U platform -d platform"]
interval: 5s
timeout: 3s
retries: 10
# ─── Caches + object stores ────────────────────────────────────────────
redis:
image: redis:7-alpine
restart: unless-stopped
networks: [breakpilot-dev]
ports:
- "6379:6379"
mongo:
image: mongo:7
restart: unless-stopped
networks: [breakpilot-dev]
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: mongo-dev-pass
volumes:
- mongo-data:/data/db
healthcheck:
test: ["CMD-SHELL", "echo 'db.runCommand({ping:1})' | mongosh --quiet"]
interval: 10s
timeout: 3s
retries: 6
minio:
image: minio/minio:latest
restart: unless-stopped
networks: [breakpilot-dev]
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minio-dev
MINIO_ROOT_PASSWORD: minio-dev-pass
command: server /data --console-address ":9001"
volumes:
- minio-data:/data
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:9000/minio/health/live"]
interval: 10s
timeout: 3s
retries: 5