# 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](../../README.md#option-b-existing-traefik--wg-easy) 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: ```bash 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 - `.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 ```bash 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.` and `gw.`), 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 `, 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.