# Bootnode - Web3 Backend for Local Development # One command to run everything: docker compose up -d # # Services: # - API: http://localhost:8000 (FastAPI) # - Web: http://localhost:3001 (Next.js Dashboard) # - Postgres: localhost:5432 # - Redis: localhost:6379 # - Datastore: localhost:8123 services: # ============================================================================= # DATABASES # ============================================================================= postgres: image: postgres:16-alpine container_name: bootnode-postgres environment: POSTGRES_USER: bootnode POSTGRES_PASSWORD: bootnode POSTGRES_DB: bootnode ports: - "5432:5432" volumes: - postgres:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U bootnode"] interval: 5s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: bootnode-redis command: redis-server --appendonly yes ports: - "6379:6379" volumes: - redis:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 datastore: image: clickhouse/clickhouse-server:24-alpine container_name: bootnode-datastore environment: CLICKHOUSE_DB: bootnode CLICKHOUSE_USER: bootnode CLICKHOUSE_PASSWORD: bootnode CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1 ports: - "8123:8123" - "9000:9000" volumes: - datastore:/var/lib/clickhouse healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:8123/ping"] interval: 5s timeout: 5s retries: 5 # ============================================================================= # SEARCH & VECTOR DATABASES (Planet-Scale Query Layer) # ============================================================================= # Qdrant - Vector Search for semantic queries (similar contracts, addresses) qdrant: image: qdrant/qdrant:latest container_name: bootnode-qdrant ports: - "6333:6333" - "6334:6334" volumes: - qdrant:/qdrant/storage environment: QDRANT__SERVICE__GRPC_PORT: 6334 QDRANT__LOG_LEVEL: INFO # qdrant image ships no wget/curl; readiness is gated via depends_on: # service_started + the api's own startup checks instead of a broken healthcheck. # Meilisearch - Full-text search for addresses, tokens, contracts meilisearch: image: getmeili/meilisearch:v1.6 container_name: bootnode-meilisearch ports: - "7700:7700" volumes: - meilisearch:/meili_data environment: MEILI_ENV: development MEILI_NO_ANALYTICS: "true" # Set a master key in production! # MEILI_MASTER_KEY: your-master-key healthcheck: test: ["CMD", "curl", "-f", "http://localhost:7700/health"] interval: 5s timeout: 5s retries: 5 # ============================================================================= # BOOTNODE SERVICES # ============================================================================= api: build: context: ./api dockerfile: Dockerfile container_name: bootnode-api environment: APP_ENV: development DEBUG: "true" DATABASE_URL: postgresql+asyncpg://bootnode:bootnode@postgres:5432/bootnode REDIS_URL: redis://redis:6379/0 DATASTORE_URL: clickhouse://bootnode:bootnode@datastore:8123/bootnode QDRANT_URL: http://qdrant:6333 MEILISEARCH_URL: http://meilisearch:7700 INDEXER_URL: http://indexer:5000 JWT_SECRET: dev-jwt-secret-change-in-production API_KEY_SALT: dev-api-key-salt # Multi-tenant off for local dev ENABLE_MULTI_TENANT: "false" # Hanzo KMS (optional for local dev - uses env vars directly) HANZO_KMS_URL: ${HANZO_KMS_URL:-https://kms.hanzo.ai} HANZO_KMS_ORG: ${HANZO_KMS_ORG:-hanzo} HANZO_KMS_PROJECT: ${HANZO_KMS_PROJECT:-bootnode} HANZO_KMS_ENV: ${HANZO_KMS_ENV:-development} HANZO_KMS_CLIENT_ID: ${HANZO_KMS_CLIENT_ID:-} HANZO_KMS_CLIENT_SECRET: ${HANZO_KMS_CLIENT_SECRET:-} # Public RPC endpoints ETH_MAINNET_RPC: https://eth.llamarpc.com ETH_SEPOLIA_RPC: https://ethereum-sepolia-rpc.publicnode.com BASE_MAINNET_RPC: https://mainnet.base.org POLYGON_MAINNET_RPC: https://polygon-rpc.com ARBITRUM_MAINNET_RPC: https://arb1.arbitrum.io/rpc OPTIMISM_MAINNET_RPC: https://mainnet.optimism.io AVALANCHE_MAINNET_RPC: https://api.avax.network/v1/bc/C/rpc BSC_MAINNET_RPC: https://bsc-dataseed.binance.org SOLANA_MAINNET_RPC: https://api.mainnet-beta.solana.com ports: - "8000:8000" depends_on: postgres: condition: service_healthy redis: condition: service_healthy qdrant: condition: service_started meilisearch: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 10s timeout: 5s retries: 3 volumes: - ./api:/app:cached # Mount Docker socket for node deployment - /var/run/docker.sock:/var/run/docker.sock extra_hosts: - "host.docker.internal:host-gateway" restart: unless-stopped web: build: context: ./console dockerfile: Dockerfile # No build args. Brand and API origin are resolved from the hostname at # runtime (console/lib/brand.ts). To aim a local browser at a local API, set # `bootnode_api_url` in localStorage — the one escape hatch, documented # in console/lib/api.ts. container_name: bootnode-web environment: NODE_ENV: production PORT: "3001" ports: - "3001:3001" depends_on: - api restart: unless-stopped # ============================================================================= # OPTIONAL: MONITORING (docker compose --profile monitoring up -d) # ============================================================================= grafana: image: grafana/grafana:latest container_name: bootnode-grafana profiles: ["monitoring"] environment: GF_SECURITY_ADMIN_PASSWORD: admin GF_USERS_ALLOW_SIGN_UP: "false" ports: - "3000:3000" volumes: - grafana:/var/lib/grafana depends_on: - api # ============================================================================= # OPTIONAL: INDEXER (docker compose --profile indexer up -d) # ============================================================================= indexer: build: context: ./indexer dockerfile: Dockerfile container_name: bootnode-indexer profiles: ["indexer"] environment: DATABASE_URL: postgres://bootnode:bootnode@postgres:5432/bootnode DATASTORE_URL: clickhouse://bootnode:bootnode@datastore:8123/bootnode REDIS_URL: redis://redis:6379/0 depends_on: postgres: condition: service_healthy datastore: condition: service_healthy restart: unless-stopped # ============================================================================= # OPTIONAL: SUBGRAPH (docker compose --profile subgraph up -d) # Graph Node + IPFS + dedicated Postgres for subgraph indexing # ============================================================================= graph-postgres: image: postgres:16-alpine container_name: bootnode-graph-postgres profiles: ["subgraph"] environment: POSTGRES_USER: graph POSTGRES_PASSWORD: graph-bootnode-2026 POSTGRES_DB: graph_node POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" ports: - "15432:5432" volumes: - graph-postgres:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U graph"] interval: 5s timeout: 5s retries: 5 graph-ipfs: image: ipfs/kubo:v0.24.0 container_name: bootnode-graph-ipfs profiles: ["subgraph"] ports: - "15001:5001" - "18080:8080" volumes: - graph-ipfs:/data/ipfs environment: IPFS_PROFILE: server healthcheck: test: ["CMD-SHELL", "ipfs dag stat /ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn || exit 1"] interval: 10s timeout: 5s retries: 5 graph-node: image: graphprotocol/graph-node:v0.35.1 container_name: bootnode-graph-node profiles: ["subgraph"] environment: postgres_host: graph-postgres postgres_port: 5432 postgres_user: graph postgres_pass: graph-bootnode-2026 postgres_db: graph_node ipfs: graph-ipfs:5001 ethereum: "lux:${LUX_RPC_URL:-http://host.docker.internal:9630/v1/bc/C/rpc} zoo:${ZOO_RPC_URL:-http://host.docker.internal:9630/v1/bc/zoo/rpc} pars:${PARS_RPC_URL:-http://host.docker.internal:9630/v1/bc/pars/rpc} hanzo:${HANZO_RPC_URL:-http://host.docker.internal:9630/v1/bc/hanzo/rpc} spc:${SPC_RPC_URL:-http://host.docker.internal:9630/v1/bc/spc/rpc}" GRAPH_LOG: info ETHEREUM_REORG_THRESHOLD: 1 ETHEREUM_ANCESTOR_COUNT: 1 node_id: graph_node ports: - "18000:8000" # GraphQL queries - "18001:8001" # GraphQL websocket - "18020:8020" # Admin (deploy/create subgraphs) - "18030:8030" # Status extra_hosts: - "host.docker.internal:host-gateway" depends_on: graph-postgres: condition: service_healthy graph-ipfs: condition: service_healthy restart: unless-stopped volumes: postgres: redis: datastore: qdrant: meilisearch: grafana: graph-postgres: graph-ipfs: