services: control-plane: build: ./control_plane # CI (.forgejo/workflows/build-image.yml) publishes this same build to # the registry on every push to master, tagged `latest` plus the short # commit sha. `docker compose up` builds fresh from `build:` above and # tags the result as this name, ignoring this being a registry ref; # `docker stack deploy` (Swarm ignores `build:` entirely, see the # Swarm caveats in README.md) pulls it instead — no manual # `docker build -t ... ./control_plane` step needed there anymore. image: ${GATEWAY_CONTROL_PLANE_IMAGE:-forgejo.sourcegate.online/robert/furtka-gateway-control-plane:latest} 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} # Blank means no explicit resolver (`tls: {}`) — Traefik applies # whatever default TLS is already configured on YOUR Traefik. Set # this to your Traefik's certResolver name if it doesn't have one. - GATEWAY_CERT_RESOLVER=${GATEWAY_CERT_RESOLVER:-} # Your existing wg-easy's admin API — reachable at whatever # host:port makes sense for your setup (a sibling container on the # network this joins — see `networks:` below — or a real host:port # if it's elsewhere). See the README's Prerequisites checklist for # the network-reachability requirement this doesn't (can't) # automate away. - WG_EASY_URL=${WG_EASY_URL} - WG_EASY_ADMIN_PASSWORD=${WG_EASY_ADMIN_PASSWORD} # "14" (default) or "15" — these are different, incompatible APIs # (see control_plane/wgeasy.py vs wgeasy_v15.py's module docstrings). # Check which one your wg-easy image tag actually is. - WG_EASY_API_VERSION=${WG_EASY_API_VERSION:-14} # v15 only: the admin user's username (v14 has no username concept — # see WG_EASY_ADMIN_PASSWORD above). - WG_EASY_ADMIN_USERNAME=${WG_EASY_ADMIN_USERNAME:-} # v15 only: restricts new clients' AllowedIPs to your wg-easy peer # subnet (v15 defaults new clients to full-tunnel 0.0.0.0/0, ::/0 # otherwise — see wgeasy_v15.py's restrict_default_allowed_ips()). - GATEWAY_WG_ALLOWED_IPS=${GATEWAY_WG_ALLOWED_IPS:-10.8.0.0/24} volumes: - control_plane_data:/data # Bind-mount this at whatever host path YOUR Traefik's file provider # already watches (or add a second `providers.file` directory to # your Traefik's static config pointed at this same path — additive, # doesn't touch your existing dynamic config). Default here is just # a placeholder; ops/deploy.sh asks for the real path. - ${GATEWAY_DYNAMIC_DIR_HOST:-./dynamic}:/dynamic ports: # Loopback-only by default. This is how a remote Furtka box reaches # the registration API in the COMMON case (your own Traefik, on the # network below, proxies to it — see # traefik/dynamic/control-plane.yml.example) — a public bind here # isn't needed. Only widen GATEWAY_CONTROL_PLANE_BIND if your # Traefik genuinely can't join this container's docker network (a # separate-host setup) — ops/deploy.sh flags the access-control # implication of doing that when you choose it. - "${GATEWAY_CONTROL_PLANE_BIND:-127.0.0.1:8090}:8090" restart: unless-stopped # No custom `networks:` here on purpose — this stays on the default # bridge network compose creates unless you also run YOUR Traefik in # this SAME compose project. To reach a Traefik running elsewhere (its # own compose project, or your own homelab stack), ops/deploy.sh joins # this container to your Traefik's existing docker network after # `up` via `docker network connect` — no external-network dependency # baked into this file, so it works whether or not that network # exists yet at compose-up time. volumes: control_plane_data: