First Home Gateway Setup
You have a NAS, a mini PC or a Raspberry Pi in your home network and want to make services on it (router admin, NAS WebUI, Home Assistant, …) reachable through your GateControl instance. Without port forwarding on the router, without detours.
The solution is the home gateway container (ghcr.io/callmetechie/gatecontrol-gateway):
a slim companion that builds an outbound WireGuard tunnel to the server
and acts as a forwarder in the LAN. The server proxy can then
reach any LAN target via target_kind=gateway, and the gateway
performs the final hop.
This guide takes you through it once: create peer → pull gateway.env
→ docker-compose on the NAS → check the first heartbeat.
Duration: ~10 minutes.
What you need beforehand
- A running GateControl instance with a Pro license (home gateway is a Pro feature).
- Admin access to the GateControl web UI.
- A host machine in the home network running Docker. Typical candidates:
- Synology DSM 7.2+ with Container Manager
- Raspberry Pi 4/5 with Raspberry Pi OS
- Ubuntu/Debian box
- Proxmox LXC with nesting + keyctl
/dev/net/tunmust be available on the host (the compose template below passes it through explicitly). On Synology and some LXC variants a one-offmodprobe tunhas to run for this.- A free LAN IP on the gateway host and access to the LAN segment of the target devices. Important: the gateway host must physically be in the same Layer 2 network as the devices you want to reach — otherwise Wake-on-LAN won't work.
No port forward on the router, no firewall opening, no public IP needed. The tunnel goes from the gateway to the server over UDP 51820.
1. Create the gateway peer in the admin UI
- Open the admin UI and go to Peers & Clients.
- Click on + Add (top right).
- In the modal:
- Peer Name: e.g.
homeserver-nucornas-syno— that's the display name in the admin UI. - Description (optional):
Intel NUC · Debian 12 · living room. - Tick Home Gateway. The Gateway API Port
field appears and stands at
9876. Leave the default, as long as the port on the host is not already occupied.
- Peer Name: e.g.
- Click Save.
After saving, the UI shows a one-off dialog with the
pairing tokens (api_token and push_token). The tokens are shown in
plaintext only this one time — the server only stores hashes.
Leave the modal open until step 2.
2. Download gateway.env
In the same dialog there is a Download gateway config button.
The file is called gateway.env and contains everything the gateway container
needs:
GC_SERVER_URL=https://gate.example.com
GC_API_TOKEN=...
GC_GATEWAY_TOKEN=...
GC_TUNNEL_IP=10.8.0.42
GC_API_PORT=9876
GC_HEARTBEAT_INTERVAL_S=30
WG_PRIVATE_KEY=...
WG_PRESHARED_KEY=...
WG_ENDPOINT=gate.example.com:51820
WG_SERVER_PUBLIC_KEY=...
WG_ADDRESS=10.8.0.42/32
WG_ALLOWED_IPS=10.8.0.1/32
WG_DNS=10.8.0.1
Important:
- Save the file immediately in a password manager or directly on the gateway host.
- If you lose the
.env: open peer edit → click Download ENV again. This rotates both tokens — the old gateway loses the connection and must be redeployed. WG_ADDRESS=10.8.0.42/32is deliberately a /32 (not /24). That prevents routing conflicts if in parallel on the same machine a normal GateControl client is running.
3. docker-compose on the gateway host
On the NAS/Pi/NUC create a folder, e.g. /opt/gatecontrol-gateway/.
Inside docker-compose.yml:
services:
gateway:
image: ghcr.io/callmetechie/gatecontrol-gateway:latest
container_name: gatecontrol-gateway
restart: unless-stopped
network_mode: host
cap_drop:
- ALL
cap_add:
- NET_ADMIN # wg-quick, iptables, Routing
- NET_BIND_SERVICE # L4-Ports <1024 (SSH, DNS, HTTP)
read_only: true
user: "0:0"
devices:
- /dev/net/tun:/dev/net/tun
tmpfs:
- /tmp
- /run
- /etc/wireguard
volumes:
- ./config:/config:ro
environment:
- LOG_LEVEL=info
- GATEWAY_ENV_PATH=/config/gateway.env
- WG_QUICK_USERSPACE_IMPLEMENTATION=wireguard-go
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Explanations of the most important points:
network_mode: hostis mandatory. The gateway binds L4 routes dynamically on changing ports — that wouldn't work with Docker NAT without restart.cap_drop: ALL+ two explicit caps is the security baseline.NET_ADMINis needed by wg-quick,NET_BIND_SERVICEonly if you run L4 routes on ports < 1024.read_only: truewith tmpfs for/tmp,/run,/etc/wireguard— wg-quick writes config at runtime into/etc/wireguard.user: "0:0"is deliberately root: wg-quick is a shell script (line 85) that goes nowhere if UID != 0. The security boundary is the caps, not the UID.WG_QUICK_USERSPACE_IMPLEMENTATION=wireguard-goforces the userspace WireGuard implementation. On Synology DSM and in LXC containers without a WG kernel module, that's the only way.
Then put the gateway.env from step 2 into ./config/gateway.env
(i.e. next to the compose file, in a config/ subfolder).
mkdir -p config
mv ~/Downloads/gateway.env config/gateway.env
chmod 600 config/gateway.env
4. Start the container
docker compose up -d
docker compose logs -f gateway
In the first few seconds you'll see:
- WG setup (
[#] ip link add wg0 type wireguard…) - Heartbeat message:
POST /api/v1/gateway/heartbeat → 200 config sync: version N applied
Give the process ~30 seconds until the first heartbeat.
5. Check in the admin UI
Back in the admin UI, on Peers & Clients. In the Home Gateways section your gateway card should now appear with a green status dot as Online.
Click on the card — in the detail panel you see:
- Last seen (should be seconds ago)
- WG handshake (time, fresh)
- Uptime + container version (from
gateway_info.section_version) - Routed targets (still empty — you create routes in the next step)
6. Create the first gateway route
As soon as the gateway is online, you can add LAN hosts as a route. That
is the standard case: HTTP route on e.g. dsm.example.com → target_kind
gateway → select gateway peer → LAN host 192.168.1.10:5001 → done.
Detailed walkthrough with all fields and DNS/TLS subtleties: see adding-a-route.md.
For Remote Desktop access to Windows machines in the same LAN: see configuring-rdp.md.
Troubleshooting
Gateway stays offline
-
Look at the container logs:
docker compose logs --tail 200 gatewayTypical messages:
GC_SERVER_URL unreachable→ DNS/network from the gateway to the server broken.401 unauthorized→ tokens ingateway.envdon't match the DB. Tokens rotated or wrong.envplaced?TLS alert 80→ server has an SSL problem; see certificates.md.
-
Simulate the heartbeat via curl from the gateway host:
curl -i -X POST https://gate.example.com/api/v1/gateway/heartbeat \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(grep ^GC_GATEWAY_TOKEN config/gateway.env | cut -d= -f2)" \ -d '{"peer_id":42,"status":"ok"}'200 OK → network is good, problem lies in the container code. Connection refused / timeout → firewall/DNS.
-
Check
.envfor corruption (e.g. Windows line endings after copy-paste via Windows tools):file config/gateway.env # should say "ASCII text", not "CRLF"
WG tunnel doesn't come up
Symptom: logs show wg-quick: not a valid key or Handshake did not complete after 5 seconds.
Check steps:
- Server endpoint reachable? From the gateway host:
nc -u -v gate.example.com 51820(must be UDP —ncwithout-uis TCP and says "open" although UDP is closed). - GC_BASE_URL set correctly in the server? If the
gateway.envcomes with placeholder domaingatecontrol.example.com, you haven't setGC_BASE_URLin the server.env. - Firewall between gateway host and internet: UDP 51820 outbound must be free. ICMP is not a sufficient test.
/dev/net/tunpresent?
If "No such file or directory": rundocker exec -it gatecontrol-gateway ls -la /dev/net/tunmodprobe tunon the host and restart the container.
Config hash out of sync
Symptom: new route in the admin UI does not appear in the gateway.
Cause: gateway uses pull + push sync. Push comes from the server (OK),
pull every GC_POLL_INTERVAL_S seconds (default 300 = 5 min).
Fix: in the admin UI trigger Reload on the gateway card — calls the
push endpoint directly. Alternatively once docker compose restart gateway.
"Port 9876 already in use"
The health API port (GC_API_PORT in the gateway.env) collides with
something else on the host. Edit the peer in the admin UI, set a
different port (e.g. 19876), re-download .env.
Further reading
- adding-a-route.md — map a domain to a gateway LAN host.
- configuring-rdp.md — RDP over gateway routing.
- ../concepts/home-gateway.md — technical internals of push+pull sync, self-check layer, heartbeat protocol.
- ../API.md —
/api/v1/gateway/…endpoints for automation.