furtka-gateway/docker-compose.yaml
Robert Syrnicki 6b8cc6e006 fix: make ops/deploy.sh rebuild, dry-run safely, and join Traefik at start
- Always build from the checkout (`up --build`, `pull_policy: build`)
  instead of reusing a stale local image or pulling an old registry one.
- --render-only writes to rendered/ and no longer touches the live
  Traefik dynamic directory; the route is installed after health check.
- Join the Traefik network via a generated docker-compose.override.yaml
  rather than `docker network connect` after startup, which raced the
  control-plane's startup call to wg-easy and was lost on recreate.
- Add --non-interactive, check the public HTTPS endpoint, and point the
  closing instructions at Settings -> Gateway (the suggested
  `furtka gateway connect` command doesn't exist).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:49:49 +02:00

66 lines
3.6 KiB
YAML

services:
control-plane:
build: ./control_plane
# Always build from this checkout — never pull a registry image that may
# be older than the code you just pulled. The image name below only
# matters for `docker stack deploy` (Swarm can't build; see the Swarm
# caveats in README.md), which pulls what CI publishes on every push to
# master (`latest` plus the short commit sha).
pull_policy: build
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 `networks:` here on purpose — your Traefik's network name differs
# per setup. ops/deploy.sh writes a gitignored
# docker-compose.override.yaml (auto-loaded by `docker compose`) that
# joins this container to it from the first second.
volumes:
control_plane_data: