# OpenZeppelin Symbiotic Templates - Docker Compose # # Base configuration for all environments (external networks by default). # For local anvil development, layer docker-compose.local.yml on top. # # Profiles: # - infra: Infrastructure services only # - dev: All services # # Usage: # make start # Full local stack only (ENV=local) # make stop # Stop all (preserve state) # make clean # Full reset name: symbiotic-template services: # ═══════════════════════════════════════════════════════════════════════════ # OZ MONITOR - Event watching # ═══════════════════════════════════════════════════════════════════════════ oz-monitor: image: openzeppelin/openzeppelin-monitor:v1.5.0 container_name: oz-monitor profiles: [dev] environment: METRICS_PORT: "${METRICS_PORT:-8081}" METRICS_ENABLED: "${METRICS_ENABLED:-false}" LOG_LEVEL: "${LOG_LEVEL:-debug}" IN_DOCKER: "true" # Multi-operator webhook fanout WEBHOOK_URL_1: "http://operator-1:3000/webhook/events" WEBHOOK_URL_2: "http://operator-2:3000/webhook/events" WEBHOOK_URL_3: "http://operator-3:3000/webhook/events" WEBHOOK_SECRET: "${WEBHOOK_SECRET}" restart: on-failure:5 mem_swappiness: 0 security_opt: - no-new-privileges volumes: - ./generated/${ENV:-local}/oz-monitor:/app/config/:ro - ./data/oz-monitor:/app/data networks: - bridge-network # ═══════════════════════════════════════════════════════════════════════════ # REDIS - Job queue for OZ Relayer # ═══════════════════════════════════════════════════════════════════════════ redis: image: redis:bookworm container_name: redis profiles: [dev] ports: - "6379:6379" volumes: - redis-data:/data command: redis-server --appendonly yes --save 60 1 networks: - bridge-network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s start_period: 5s retries: 3 restart: unless-stopped # ═══════════════════════════════════════════════════════════════════════════ # OZ RELAYER - Transaction submission # ═══════════════════════════════════════════════════════════════════════════ oz-relayer: image: openzeppelin/openzeppelin-relayer:v1.4.0 container_name: oz-relayer profiles: [dev] ports: - "8080:8080" environment: REDIS_URL: redis://redis:6379 RATE_LIMIT_REQUESTS_PER_SECOND: "100" RATE_LIMIT_BURST: "200" API_KEY: "${OZ_RELAYER_API_KEY}" WEBHOOK_SIGNING_KEY: "${OZ_RELAYER_WEBHOOK_SECRET}" KEYSTORE_PASSPHRASE: "${KEYSTORE_PASSPHRASE}" volumes: - ./config/oz-relayer:/app/config:ro - ./config/keys/${ENV:-local}:/app/keys:ro - ./generated/${ENV:-local}/oz-relayer/config.json:/app/config/config.json:ro - ./generated/${ENV:-local}/oz-relayer/networks:/app/config/networks:ro networks: - bridge-network depends_on: redis: condition: service_healthy healthcheck: test: [ "CMD", "node", "-e", "require('http').get({hostname:'localhost',port:8080,path:'/api/v1/health',headers:{'Authorization':'Bearer '+process.env.API_KEY}}, r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))", ] interval: 10s timeout: 5s start_period: 10s retries: 3 restart: unless-stopped # ═══════════════════════════════════════════════════════════════════════════ # SYMBIOTIC RELAY SIDECARS - BLS signing # ═══════════════════════════════════════════════════════════════════════════ symbiotic-relay-1: image: symbioticfi/relay:1.1.1 container_name: symbiotic-relay-1 profiles: [dev] command: ["/config/start-sidecar.sh", "1"] cpus: 0.25 env_file: - path: generated/${ENV:-local}/sidecar-1.env required: false volumes: - ./scripts/symbiotic-relay:/config:ro - ./data/sidecar-1:/storage ports: - "8081:8080" - "8881:8880" networks: - bridge-network healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"] interval: 5s timeout: 3s retries: 30 start_period: 60s restart: on-failure:5 symbiotic-relay-2: image: symbioticfi/relay:1.1.1 container_name: symbiotic-relay-2 profiles: [dev] command: ["/config/start-sidecar.sh", "2"] cpus: 0.25 env_file: - path: generated/${ENV:-local}/sidecar-2.env required: false volumes: - ./scripts/symbiotic-relay:/config:ro - ./data/sidecar-2:/storage ports: - "8082:8080" - "8882:8880" networks: - bridge-network healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"] interval: 5s timeout: 3s retries: 30 start_period: 60s restart: on-failure:5 symbiotic-relay-3: image: symbioticfi/relay:1.1.1 container_name: symbiotic-relay-3 profiles: [dev] command: ["/config/start-sidecar.sh", "3"] cpus: 0.25 env_file: - path: generated/${ENV:-local}/sidecar-3.env required: false volumes: - ./scripts/symbiotic-relay:/config:ro - ./data/sidecar-3:/storage ports: - "8083:8080" - "8883:8880" networks: - bridge-network healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"] interval: 5s timeout: 3s retries: 30 start_period: 60s restart: on-failure:5 # ═══════════════════════════════════════════════════════════════════════════ # OPERATORS - Event coordinators and proof submitters (x3) # ═══════════════════════════════════════════════════════════════════════════ operator-1: build: context: ./operator dockerfile: Dockerfile image: operator:latest container_name: operator-1 profiles: [dev] environment: RUST_LOG: "${LOG_LEVEL:-info},operator=debug" WEBHOOK_SECRET: "${WEBHOOK_SECRET}" OZ_RELAYER_API_KEY: "${OZ_RELAYER_API_KEY}" OZ_RELAYER_WEBHOOK_SECRET: "${OZ_RELAYER_WEBHOOK_SECRET}" SOURCE_RPC_URL: "${SOURCE_RPC_URL:-}" volumes: - ./config/environments/${ENV:-local}.json:/config/environment.json:ro - ./deployments/${ENV:-local}.json:/config/deployments.json:ro - operator-1-data:/app/data ports: - "3001:3000" networks: - bridge-network depends_on: symbiotic-relay-1: condition: service_healthy healthcheck: test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/healthz", ] interval: 30s timeout: 3s start_period: 5s retries: 3 stop_grace_period: 35s restart: unless-stopped command: ["--environment", "/config/environment.json", "--deployments", "/config/deployments.json", "--sidecar-address", "http://symbiotic-relay-1:8080", "--relayer-id", "operator-relayer-1"] operator-2: build: context: ./operator dockerfile: Dockerfile image: operator:latest container_name: operator-2 profiles: [dev] environment: RUST_LOG: "${LOG_LEVEL:-info},operator=debug" WEBHOOK_SECRET: "${WEBHOOK_SECRET}" OZ_RELAYER_API_KEY: "${OZ_RELAYER_API_KEY}" OZ_RELAYER_WEBHOOK_SECRET: "${OZ_RELAYER_WEBHOOK_SECRET}" SOURCE_RPC_URL: "${SOURCE_RPC_URL:-}" volumes: - ./config/environments/${ENV:-local}.json:/config/environment.json:ro - ./deployments/${ENV:-local}.json:/config/deployments.json:ro - operator-2-data:/app/data ports: - "3002:3000" networks: - bridge-network depends_on: symbiotic-relay-2: condition: service_healthy healthcheck: test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/healthz", ] interval: 30s timeout: 3s start_period: 5s retries: 3 stop_grace_period: 35s restart: unless-stopped command: ["--environment", "/config/environment.json", "--deployments", "/config/deployments.json", "--sidecar-address", "http://symbiotic-relay-2:8080", "--relayer-id", "operator-relayer-2"] operator-3: build: context: ./operator dockerfile: Dockerfile image: operator:latest container_name: operator-3 profiles: [dev] environment: RUST_LOG: "${LOG_LEVEL:-info},operator=debug" WEBHOOK_SECRET: "${WEBHOOK_SECRET}" OZ_RELAYER_API_KEY: "${OZ_RELAYER_API_KEY}" OZ_RELAYER_WEBHOOK_SECRET: "${OZ_RELAYER_WEBHOOK_SECRET}" SOURCE_RPC_URL: "${SOURCE_RPC_URL:-}" volumes: - ./config/environments/${ENV:-local}.json:/config/environment.json:ro - ./deployments/${ENV:-local}.json:/config/deployments.json:ro - operator-3-data:/app/data ports: - "3003:3000" networks: - bridge-network depends_on: symbiotic-relay-3: condition: service_healthy healthcheck: test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/healthz", ] interval: 30s timeout: 3s start_period: 5s retries: 3 stop_grace_period: 35s restart: unless-stopped command: ["--environment", "/config/environment.json", "--deployments", "/config/deployments.json", "--sidecar-address", "http://symbiotic-relay-3:8080", "--relayer-id", "operator-relayer-3"] networks: bridge-network: driver: bridge volumes: redis-data: operator-1-data: operator-2-data: operator-3-data: