services: # ── Temporal persistence DB ────────────────────────────────────────────────── temporal-db: image: postgres:16 environment: POSTGRES_USER: temporal POSTGRES_PASSWORD: temporal POSTGRES_DB: temporal volumes: - temporal-db-data:/var/lib/postgresql/data networks: - actcore-net healthcheck: test: ["CMD-SHELL", "pg_isready -U temporal"] interval: 5s timeout: 5s retries: 10 # ── Temporal server (PostgreSQL visibility, no Elasticsearch) ───────────────── temporal: image: temporalio/auto-setup:1.29.1 depends_on: temporal-db: condition: service_healthy environment: DB: postgres12 DB_PORT: 5432 POSTGRES_USER: temporal POSTGRES_PWD: temporal POSTGRES_SEEDS: temporal-db DYNAMIC_CONFIG_FILE_PATH: config/dynamicconfig/development-sql.yaml ENABLE_ES: "false" VISIBILITY_DBNAME: temporal_visibility TEMPORAL_ADDRESS: temporal:7233 volumes: - ./dynamicconfig:/etc/temporal/config/dynamicconfig networks: - actcore-net healthcheck: test: ["CMD", "temporal", "operator", "cluster", "health", "--address", "temporal:7233"] interval: 10s timeout: 10s retries: 20 start_period: 30s # ── Temporal Web UI ─────────────────────────────────────────────────────────── temporal-ui: image: temporalio/ui:latest depends_on: temporal: condition: service_healthy environment: TEMPORAL_ADDRESS: temporal:7233 TEMPORAL_CORS_ORIGINS: http://localhost:8080 ports: - "8080:8080" networks: - actcore-net # ── NATS with JetStream ─────────────────────────────────────────────────────── nats: image: nats:2.10-alpine command: ["-js", "-sd", "/data", "-m", "8222"] volumes: - nats-data:/data ports: - "4222:4222" - "8222:8222" networks: - actcore-net healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:8222/healthz | grep -q ok"] interval: 5s timeout: 5s retries: 10 # ── Application DB ──────────────────────────────────────────────────────────── app-db: image: postgres:16 environment: POSTGRES_USER: actcore POSTGRES_PASSWORD: actcore POSTGRES_DB: actcore volumes: - app-db-data:/var/lib/postgresql/data networks: - actcore-net healthcheck: test: ["CMD-SHELL", "pg_isready -U actcore"] interval: 5s timeout: 5s retries: 10 # ── One-shot migration runner ───────────────────────────────────────────────── actcore-migrate: build: . command: ["python", "-m", "alembic", "upgrade", "head"] env_file: .env depends_on: app-db: condition: service_healthy networks: - actcore-net restart: "no" # ── Temporal worker ─────────────────────────────────────────────────────────── actcore-worker: build: . command: ["python", "-m", "activity_core.worker"] env_file: .env depends_on: temporal: condition: service_healthy app-db: condition: service_healthy nats: condition: service_healthy actcore-migrate: condition: service_completed_successfully ports: - "9090:9090" networks: - actcore-net restart: unless-stopped # ── REST API ────────────────────────────────────────────────────────────────── actcore-api: build: . command: ["uvicorn", "activity_core.api:app", "--host", "0.0.0.0", "--port", "8010"] env_file: .env depends_on: temporal: condition: service_healthy app-db: condition: service_healthy nats: condition: service_healthy actcore-migrate: condition: service_completed_successfully ports: - "8010:8010" networks: - actcore-net restart: unless-stopped healthcheck: test: ["CMD", "python", "-c", "import urllib.request,sys; r=urllib.request.urlopen('http://localhost:8010/health'); sys.exit(0 if r.status==200 else 1)"] interval: 10s timeout: 5s retries: 5 start_period: 15s # ── Event Router ────────────────────────────────────────────────────────────── actcore-event-router: build: . command: ["python", "-m", "activity_core.event_router"] env_file: .env depends_on: temporal: condition: service_healthy app-db: condition: service_healthy nats: condition: service_healthy actcore-migrate: condition: service_completed_successfully networks: - actcore-net restart: unless-stopped volumes: temporal-db-data: app-db-data: nats-data: networks: actcore-net: driver: bridge