furtka-gateway/examples/starter-stack/README.md
Robert Syrnicki 4a11d77297 docs: document new-server and existing Traefik/wg-easy setups
Root README now lets you choose between the two paths, with requirements,
the three ways to route Traefik to WireGuard peers (including the
bridge-network recipe verified on a live Traefik v3.7 + wg-easy v15),
box connection, troubleshooting and security notes. The starter-stack
README lists server, firewall and DNS requirements.

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

4.8 KiB

furtka-gateway on a new server

Everything in one Docker Compose stack: wg-easy (WireGuard), Traefik (HTTPS reverse proxy with Let's Encrypt), and the furtka-gateway control-plane. Use this on a server that doesn't run anything else on ports 80/443. If you already run Traefik and wg-easy, see the root README instead.

internet ──443──▶ Traefik ──WireGuard tunnel──▶ Furtka box (behind NAT) ──▶ app
                     ▲
Furtka box ──51820/udp──▶ wg-easy        (box dials out; no port forwarding at home)

Server requirements

Machine Any Linux VPS, x86_64 or arm64. 1 vCPU / 512 MB RAM is plenty.
Public IP A static public IPv4 address.
Kernel WireGuard support (Linux 5.6+ — every current distro).
Software Docker Engine with the Compose plugin v2.24 or newer (docker compose version), plus git, curl, openssl.
Free ports 80/tcp, 443/tcp, 51820/udp — nothing else may use them.
Firewall Allow those three ports inbound, in the server's firewall and any provider firewall/security group. Nothing else needs to be open.

Installing Docker on a fresh Debian/Ubuntu server:

curl -fsSL https://get.docker.com | sh

DNS requirements

Easiest: one wildcard record for your domain, pointing at the server.

Type Name Value
A *.example.com your server's IPv4

That covers everything at once:

  • vpn.example.com — the WireGuard endpoint boxes dial
  • gw.example.com — the gateway API you enter on a box
  • <app>.example.com — every app a box publishes later, with no DNS change per app

No wildcard possible? Create A records for vpn. and gw., plus one per app before you publish it. Certificates use Let's Encrypt's HTTP challenge, so no DNS provider API is needed — any registrar works.

If you also add AAAA (IPv6) records, the server must be reachable over IPv6 on the same three ports.

Install

git clone https://forgejo.sourcegate.online/robert/furtka-gateway.git
cd furtka-gateway/examples/starter-stack
./deploy.sh

It asks for four things — your domain, the two hostnames (defaults vpn.<domain> and gw.<domain>), and an email for Let's Encrypt — then generates all passwords and tokens into .env, starts the stack, checks DNS and health, and prints what to enter on a box.

./deploy.sh --render-only writes .env and dynamic/control-plane.yml without starting anything. ./deploy.sh --non-interactive takes every value from an existing .env, for scripted installs.

Connect a box

On the Furtka box: Settings → Gateway, enter

  • Gateway URL: https://gw.example.com
  • Registration token: GATEWAY_BOX_TOKEN from .env
  • Box name: anything

Then publish an app from the Apps page: ⋯ → Expose to Internet, and give it a hostname like tools.example.com.

Day to day

Task Command (in this folder)
Update git pull && ./deploy.sh --non-interactive
Logs docker compose logs -f control-plane (or wg-easy, traefik)
Restart docker compose restart
wg-easy admin UI ssh -L 51821:127.0.0.1:51821 <server>, then open http://localhost:51821 — user admin, password WG_EASY_ADMIN_PASSWORD from .env
Backup .env, dynamic/, and the Docker volumes *_wg_easy_data, *_control_plane_data, *_traefik_acme

Changed the wg-easy password in its UI? Put the new one in .env as WG_EASY_ADMIN_PASSWORD and run ./deploy.sh --non-interactive.

How it works

wg-easy creates the wg0 interface and the route to the boxes (10.8.0.0/24) inside its own network namespace. Traefik must be in that same namespace to reach a box's tunnel address. So both join one namespace owned by gateway-net, a do-nothing container whose only job is to hold the namespace and publish the ports. Because it never restarts, wg-easy and Traefik can each restart independently without cutting the other off. No host routes, NAT rules, sysctls or host networking are needed, and wg-easy's admin UI is never published to the internet.

Two consequences:

  • Restart through Compose. docker compose restart handles everything. If furtka-gateway-net itself was restarted with a bare docker restart, wg-easy and Traefik lose their network — fix with docker compose restart (plain docker compose up -d does not repair it).
  • Compose only, not Swarm. network_mode: service: isn't supported by docker stack deploy.

Verified with a simulated box: route publishing, plus restarts of wg-easy, Traefik and gateway-net, and a full stack stop/start. A real server reboot hasn't been tested yet — after your first reboot, check that https://gw.example.com/healthz answers.