services: wg-easy: image: ghcr.io/wg-easy/wg-easy:14 container_name: furtka-gateway-wg-easy environment: - WG_HOST=${GATEWAY_PUBLIC_HOST} - PASSWORD_HASH=${WG_EASY_PASSWORD_HASH} - PORT=51821 - WG_PORT=51820 volumes: - wg_easy_data:/etc/wireguard ports: - "51820:51820/udp" cap_add: - NET_ADMIN - SYS_MODULE sysctls: - net.ipv4.ip_forward=1 - net.ipv4.conf.all.src_valid_mark=1 restart: unless-stopped networks: - internal # wg-easy's own admin UI/API (port 51821) is intentionally NOT published # to the host. Only control-plane, on the internal network, talks to it. traefik: image: traefik:v3.1 container_name: furtka-gateway-traefik command: - --configFile=/etc/traefik/traefik.yml volumes: - ./traefik/static/traefik.${GATEWAY_MODE:-single}.yml:/etc/traefik/traefik.yml:ro - ./traefik/dynamic:/etc/traefik/dynamic:ro - traefik_acme:/acme ports: - "80:80" - "443:443" restart: unless-stopped networks: - internal # Traefik's own dashboard/API is disabled in both static configs # (api.dashboard: false, api.insecure: false) — never reachable at all, # let alone publicly. control-plane: build: ./control_plane container_name: furtka-gateway-control-plane environment: - GATEWAY_MODE=${GATEWAY_MODE:-single} - GATEWAY_STATE_DIR=/data - GATEWAY_DYNAMIC_DIR=/dynamic - GATEWAY_ADMIN_TOKEN=${GATEWAY_ADMIN_TOKEN} - GATEWAY_BOX_TOKEN=${GATEWAY_BOX_TOKEN} - WG_EASY_URL=http://wg-easy:51821 - WG_EASY_ADMIN_USERNAME=${WG_EASY_ADMIN_USERNAME:-admin} - WG_EASY_ADMIN_PASSWORD=${WG_EASY_ADMIN_PASSWORD} volumes: - control_plane_data:/data - ./traefik/dynamic:/dynamic ports: - "127.0.0.1:8090:8090" restart: unless-stopped networks: - internal depends_on: - wg-easy - traefik networks: internal: driver: bridge volumes: wg_easy_data: traefik_acme: control_plane_data: