# Phase 1 local development/testing stack — Postgres + egress-proxy + provisioner, all on one # machine (docs/LAB_ARCHITECTURE.md §1's documented dev exception). Nothing here represents the # production two-VM topology; it's what makes the provisioner testable on a laptop. # # provisioner and egress-proxy run with network_mode: host + NET_ADMIN/NET_RAW because: # - egress.js manipulates the host's DOCKER-USER iptables chain directly (§5) — that's only # possible if the process can see the host network namespace. # - egress-proxy needs to be reachable from every per-student bridge network's gateway IP, # which only works if it's bound across all host interfaces (host networking), not sandboxed # inside its own compose-managed bridge network. # Because provisioner uses host networking, it reaches Postgres via localhost:5433 (Postgres's # published host port), not the compose service name. services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: rootnode_platform ports: - "5433:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 10 egress-proxy: build: ../egress-proxy network_mode: host # Squid listens on :3128 across every host interface, including each per-student bridge's # gateway — that's the whole reason this needs host networking rather than a normal # compose network. provisioner: build: . network_mode: host cap_add: - NET_ADMIN - NET_RAW volumes: - /var/run/docker.sock:/var/run/docker.sock environment: PLATFORM_DB_URL: postgres://postgres:postgres@localhost:5433/rootnode_platform PROVISIONER_SHARED_SECRET: ${PROVISIONER_SHARED_SECRET:?set a real secret, do not use a default} PROVISIONER_PORT: "4100" LAB_IMAGE_REF: ${LAB_IMAGE_REF:-rootnode-academy:test} EGRESS_PROXY_PORT: "3128" DNS_RESOLVER: "127.0.0.11" NO_PROXY_CIDRS: "10.42.0.0/16,10.43.0.0/16" depends_on: postgres: condition: service_healthy