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>
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 dialgw.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_TOKENfrom.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 restarthandles everything. Iffurtka-gateway-netitself was restarted with a baredocker restart, wg-easy and Traefik lose their network — fix withdocker compose restart(plaindocker compose up -ddoes not repair it). - Compose only, not Swarm.
network_mode: service:isn't supported bydocker 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.