Tailscale calls their Relay system DERP it consists of multiple worldwide Deployed Nodes which Provide connectivity between your clients if they are unable to communicate directly to each other. Tailscale provides a Package which allows to Self-Host a DERP Server.
Why?
out of curiosity i decided to deploy my own DERP Server.
How?
i had a spare Node from Oracle’s Always Free System and deployed the DERP to one of those x86 Nodes. The node has 1vCPU (2Threads), 1GB RAM and is public facing with IPv4 and IPv6 on a Rocky Linux 9.5.
Golang
Since Tailscale doesn’t provide a Binary for the Derp Server you have to build it yourself, unfortanetly Rocky only ships golang 1.22.9 at the time of writing this but we need golang 1.23.x we have to fetch an up-to-date Version.
1
2
3
|
wget https://go.dev/dl/go1.23.4.linux-amd64.tar.gz -O /tmp/go1.23.4.linux.tar.gz
sudo tar -C /usr/local -xzf /tmp/go1.23.4.linux.tar.gz
sudo ln -s /usr/local/go/bin/go /usr/local/bin/go
|
a go version should output the current version
1
2
|
$ go version
go version go1.23.4 linux/amd64
|
User and Path
As a good practice the Service shouldn’t run as root so we will create a User for it.
1
|
sudo useradd --system --create-home --home-dir /opt/derp --shell /bin/bash derp
|
To test if the user can execute the go binary. sudo -u derp -i go version should output the same Version as before.
Build DERP
to Build the DERP pkg
1
|
sudo -u derp -i go install tailscale.com/cmd/derper@latest
|
depending on your System this could take a moment.
After building the binary a sudo -u derp -i /opt/derp/go/bin/derper --version should provide the current version 1.78.3-ERR-BuildInfo
SystemD
Since we want to let DERP run a Service we will create a SystemD for it. Update your Environment=DOMAIN=<your DOMAIN> to a DNS Name which is associated with the Public IPs of your Server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
sudo tee /etc/systemd/system/derper.service <<'EOF'
[Unit]
Description=DERP Server
After=network.target
After=tailscaled.service
[Service]
User=derp
Group=derp
Environment=DOMAIN=derp.example.com
Environment=DIRECTORY=/opt/derp
ExecStart=/bin/bash -c "${DIRECTORY}/go/bin/derper -c ${DIRECTORY}/derp.conf -hostname ${DOMAIN} -verify-clients -verify-client-url-fail-open false -stun -a :80,:443"
Restart=always
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
EOF
|
Reload your SystemD sudo systemctl daemon-reload and start the Service with sudo systemctl start derper
a systemctl status derper should look like this.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
● derper.service - DERP Server
Loaded: loaded (/etc/systemd/system/derper.service; enabled; preset: disabled)
Active: active (running) since Tue 2024-12-23 16:16:16 CET; 8s ago
Main PID: 37200 (derper)
Tasks: 7 (limit: 4380)
Memory: 4.2M
CPU: 141ms
CGroup: /system.slice/derper.service
└─37200 /opt/derp/go/bin/derper -c /opt/derp/derp.conf -hostname derp.example.com -verify-clients -verify-client-url-fail-open false -stun -a :80,:443
Dec 23 16:16:16 oci-x-002 systemd[1]: Started DERP Server.
Dec 23 16:16:17 oci-x-002 bash[37200]: 2024/12/23 16:16:17 STUN server listening on [::]:3478
Dec 23 16:16:17 oci-x-002 bash[37200]: 2024/12/23 16:16:17 derper: serving on :443 with TLS
|
Tailscale ACL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
{
...
derpMap": {
"OmitDefaultRegions": true, // controls if the default Derp Servers are used
"Regions": {
"901": {
"RegionID": 901,
"RegionCode": "OCI",
"RegionName": "OCI",
"Nodes": [
{
"Name": "oci-fra",
"RegionID": 901,
"HostName": "<your FQDN>",
"IPv4": "<your IPv4 IP>",
"IPv6": "<your IPv6 IP>",
"DerpPort": 443,
"CanPort80": true,
"StunPort": 3478,
},
],
},
},
},
...
}
|
Verify
After a short while your Tailscale Nodes should fetch the Configuration, update their DERP-Map and will try to connect to your new DERP Server.
a tailscale netcheck on a node should provide a similar output
1
2
3
4
5
6
7
8
9
10
11
12
|
$ tailscale netcheck
Report:
* Time: 2024-12-23T15:29:57.967706777Z
* UDP: true
* IPv4: yes, ...:44826
* IPv6: yes, [...]:35759
* MappingVariesByDestIP:
* PortMapping:
* Nearest DERP: OCI
* DERP latency:
- OCI: 700µs (OCI)
|