DERP server setup¶
Runbook for deploying DERP (Designated Encrypted Relay for Packets) servers. DERP servers relay WireGuard traffic when nodes cannot establish a direct peer-to-peer connection. They see only encrypted ciphertext and cannot read the traffic they relay. Deploying custom DERP servers in Helsinki and Nuremberg keeps relay traffic within Golem Trust’s infrastructure and within the EU, satisfying the data residency aspect of Mr. Bent’s requirements.
Why two regions¶
Most nodes are in Helsinki. When two Helsinki nodes cannot connect directly (a rare occurrence on Hetzner’s private network but possible when workstations connect from outside), they relay through the Helsinki DERP server with minimal latency. Banking operations personnel connecting from the Royal Bank’s offices in Ankh-Morpork may route through either server depending on their network path.
The Nuremberg server provides redundancy: if the Helsinki DERP server is unavailable, relay traffic falls back to Nuremberg. Latency increases but connectivity is maintained.
DERP server instances¶
Location |
Hostname |
Hetzner region |
|---|---|---|
Helsinki |
derp-hel.golemtrust.am |
hel1 |
Nuremberg |
derp-nbg.golemtrust.am |
nbg1 |
Both run on Hetzner CX11 instances (the minimum size; DERP servers are lightweight). Both run Debian 12.
Installation¶
Tailscale provides a derper binary as part of the Tailscale distribution. Install the Tailscale package on each DERP server:
curl -fsSL https://tailscale.com/install.sh | sh
The derper binary is included at /usr/local/bin/derper or within the Tailscale package. If not present, build it from source:
apt install -y golang
go install tailscale.com/cmd/derper@latest
mv ~/go/bin/derper /usr/local/bin/derper
TLS certificates¶
Each DERP server needs a TLS certificate for its hostname. The DERP protocol requires TLS; it does not operate over plain HTTP.
On each DERP server:
apt install -y certbot python3-certbot-dns-cloudflare
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/cloudflare.ini \
-d derp-hel.golemtrust.am \
--email ponder@golemtrust.am \
--agree-tos \
--non-interactive
Adjust the domain for the Nuremberg server. Renew certificates automatically:
0 3 * * * certbot renew --quiet && systemctl reload derper
Systemd unit¶
Create /etc/systemd/system/derper.service on each DERP server:
[Unit]
Description=Tailscale DERP server
After=network-online.target
[Service]
ExecStart=/usr/local/bin/derper \
-hostname derp-hel.golemtrust.am \
-a :443 \
-http-port 80 \
-certdir /etc/letsencrypt/live/derp-hel.golemtrust.am \
-certmode manual \
-verify-clients=true
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
The -verify-clients=true flag requires connecting clients to prove they are registered with the Headscale control plane before the DERP server will relay their traffic. This prevents the DERP servers from being used as open relays by nodes not belonging to Golem Trust. Change the hostname and certificate path for the Nuremberg server.
systemctl daemon-reload
systemctl enable derper
systemctl start derper
Firewall rules¶
Each DERP server needs:
TCP 443 from
0.0.0.0/0(DERP relay traffic)UDP 3478 from
0.0.0.0/0(STUN for NAT traversal)TCP 80 from
0.0.0.0/0(HTTP to HTTPS redirect only)
DERP servers must be publicly reachable. They have no access to the private Hetzner network and no credentials for any other system. Their exposure is intentional and limited.
DERP map configuration¶
Headscale serves the DERP map to all clients. The DERP map tells nodes which relay servers exist and how to reach them. Create /etc/headscale/derp.yaml on the Headscale server:
regions:
900:
regionid: 900
regioncode: hel1
regionname: Helsinki
nodes:
- name: 900a
regionid: 900
hostname: derp-hel.golemtrust.am
ipv4: <public IP of derp-hel>
stunport: 3478
derpport: 443
901:
regionid: 901
regioncode: nbg1
regionname: Nuremberg
nodes:
- name: 901a
regionid: 901
hostname: derp-nbg.golemtrust.am
ipv4: <public IP of derp-nbg>
stunport: 3478
derpport: 443
Update the Headscale configuration to reference this file:
derp:
paths:
- /etc/headscale/derp.yaml
auto_update_enabled: false
Restart Headscale after updating the DERP map: systemctl restart headscale.
Verification¶
From a registered Tailscale node, force a DERP connection (temporarily blocking direct UDP to simulate a restricted network):
tailscale ping --tsmp derp-hel.golemtrust.am
Check the DERP server logs for the connection:
journalctl -u derper -n 50
A successful relay shows the connection being accepted and forwarded. The log does not contain any payload content; it shows only connection events and byte counts.
Monitor DERP traffic volume via the Prometheus node exporter on each DERP server. High relay volume is worth investigating; it may indicate that direct connections are failing between a pair of nodes for a reason that can be fixed.