1548 字
8 分钟

Deploy Tailscale Derper Self-Hosted Relay Server

Tailscale is a convenient mesh networking tool that connects devices across different networks, even different countries, into a single virtual LAN. When P2P hole punching fails, traffic is relayed through Tailscale’s DERP (Designated Encrypted Relay for Packets) nodes.

Since official DERP servers are primarily located outside certain regions, relay latency can be very high in some network environments, severely impacting user experience. By self-hosting a DERP server, you can significantly reduce relay latency and improve access speed.

Prerequisites#

Before starting deployment, you need:

  • Server: A server with a public IP address (preferably close to your usage region)
  • Domain: A domain name pointing to your server (e.g., derper.example.com)
  • Firewall Configuration: Open the following ports
    • TCP 80 (HTTP, for certificate validation)
    • TCP 443 (HTTPS, DERP service)
    • UDP 3478 (STUN hole punching)

If using a server in mainland China, the domain must complete ICP filing to use ports 80 and 443.

Docker deployment is simpler and faster, suitable for most users. We’ll use the fredliang/derper image, a pre-packaged Derper Docker image.

Install Docker#

If Docker and Docker Compose are not installed on your server, install them first:

Terminal window
# Install Docker (Ubuntu example)
curl -fsSL https://get.docker.com | bash
# Start Docker service
sudo systemctl enable docker
sudo systemctl start docker

Create Docker Compose Configuration#

Create a working directory and write docker-compose.yml:

Terminal window
mkdir -p /opt/derper
cd /opt/derper

Create docker-compose.yml file:

version: '3.8'
services:
derper:
image: fredliang/derper
container_name: derper
restart: always
environment:
- DERP_CERT_MODE=manual # Use manual certificates
- DERP_ADDR=:443 # DERP service port
- DERP_HTTP_PORT=80 # HTTP port (for certificate validation)
- DERP_STUN_PORT=3478 # STUN port
- DERP_DOMAIN=derper.example.com # Replace with your domain
- DERP_VERIFY_CLIENTS=true # Enable client verification
ports:
- "443:443"
- "80:80"
- "3478:3478/udp"
volumes:
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
- ./certs:/app/certs

Environment Variables#

VariableRequiredDescriptionDefault
DERP_DOMAINYesDERP server domain nameyour-hostname.com
DERP_CERT_DIRNoCertificate storage directory/app/certs
DERP_CERT_MODENoCertificate mode: manual or letsencrypt (auto)letsencrypt
DERP_ADDRNoDERP service listen address:443
DERP_STUNNoEnable STUN servicetrue
DERP_STUN_PORTNoSTUN service port3478
DERP_HTTP_PORTNoHTTP service port, set to -1 to disable80
DERP_VERIFY_CLIENTSNoVerify client identity via local Tailscale clientfalse

Using Custom Ports#

If your server’s ports 80/443 are already in use, you can use custom ports:

version: '3.8'
services:
derper:
image: fredliang/derper
container_name: derper
restart: always
environment:
- DERP_CERT_MODE=manual
- DERP_ADDR=:13477 # Custom DERP port
- DERP_HTTP_PORT=13476 # Custom HTTP port
- DERP_STUN_PORT=13478 # Custom STUN port
- DERP_DOMAIN=derper.example.com
- DERP_VERIFY_CLIENTS=true
ports:
- "13477:13477"
- "13476:13476"
- "13478:13478/udp"
volumes:
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
- ./certs:/app/certs
NOTE

When using custom ports, you’ll need to modify DERPPort and STUNPort in the Tailscale ACL configuration accordingly.

Method 2: Manual Compilation#

If you want a deeper understanding of how Derper works, or need custom compilation, choose the manual compilation method.

Install Golang#

Derper is written in Go, so you need to install Golang first (version 1.21 or higher recommended):

Terminal window
# Download Golang (1.22.0 example)
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
# Extract to /usr/local
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
# Configure environment variables
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
# Verify installation
go version

Compile Derper#

Terminal window
# Clone Tailscale repository
git clone https://github.com/tailscale/tailscale.git
cd tailscale
# Compile derper
go build cmd/derper/derper.go
# Move to system path
sudo mv derper /usr/sbin/derper

Create Systemd Service#

Create service file /etc/systemd/system/derper.service:

[Unit]
Description=Tailscale Derper
Wants=network-pre.target
After=network-pre.target NetworkManager.service systemd-resolved.service
[Service]
ExecStart=/usr/sbin/derper \
--hostname=derper.example.com \
-a :443 \
-http-port 80 \
-certmode letsencrypt \
--certdir /var/lib/derper/certs \
-verify-clients
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target

Parameter explanation:

  • --hostname: Your domain name
  • -a: DERP service listen address
  • -http-port: HTTP port for Let’s Encrypt certificate validation
  • -certmode: Certificate mode, letsencrypt for auto, manual for manual management
  • --certdir: Certificate storage directory
  • -verify-clients: Enable client verification (prevents unauthorized usage)

Start Service#

Terminal window
# Create certificate directory
sudo mkdir -p /var/lib/derper/certs
# Reload systemd configuration
sudo systemctl daemon-reload
# Start and enable auto-start
sudo systemctl enable derper
sudo systemctl start derper
# Check service status
sudo systemctl status derper

Certificate Management#

Automatic Certificate (Let’s Encrypt)#

If using standard ports 80/443, Derper can automatically request and renew Let’s Encrypt certificates:

Docker method:

environment:
- DERP_CERT_MODE=letsencrypt
- DERP_ADDR=:443
- DERP_HTTP_PORT=80

Manual compilation method:

ExecStart=/usr/sbin/derper \
--hostname=derper.example.com \
-a :443 \
-http-port 80 \
-certmode letsencrypt \
--certdir /var/lib/derper/certs

Manual Certificate Management#

If using custom ports or existing certificates, you need to manage certificates manually.

Certificate file naming convention:

  • domain.crt (full certificate chain)
  • domain.key (private key)

Example: derper.example.com.crt and derper.example.com.key

Place certificates in:

  • Docker: ./certs/ directory (same level as docker-compose.yml)
  • Manual compilation: /var/lib/derper/certs/ directory

Client Verification#

To prevent unauthorized usage of your self-hosted Derper server, it’s highly recommended to enable client verification. When enabled, only devices in your Tailscale network can use this Derper server.

Install Tailscale Client#

Install Tailscale client on the Derper server:

Terminal window
# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# Start and join network
sudo tailscale up

Enable Verification#

Docker method:

Ensure docker-compose.yml includes:

environment:
- DERP_VERIFY_CLIENTS=true
volumes:
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock

Manual compilation method:

Add -verify-clients parameter in systemd service file:

ExecStart=/usr/sbin/derper \
--hostname=derper.example.com \
-a :443 \
-http-port 80 \
-certmode letsencrypt \
--certdir /var/lib/derper/certs \
-verify-clients

Restart service:

Terminal window
sudo systemctl daemon-reload
sudo systemctl restart derper

Configure Tailscale#

Modify Access Controls#

  1. Visit Tailscale Admin Console
  2. Add custom DERP configuration in Access Controls

Add at the end of configuration file (keep other configurations):

{
// ... other configurations ...
"derpMap": {
"OmitDefaultRegions": false, // Keep official servers as backup
"Regions": {
"900": {
"RegionID": 900,
"RegionCode": "myderp",
"RegionName": "My Custom Derper",
"Nodes": [
{
"Name": "1",
"RegionID": 900,
"HostName": "derper.example.com", // Your domain
"DERPPort": 443, // Modify if using custom port
"STUNPort": 3478, // Modify if using custom port
"STUNOnly": false
}
]
}
}
}
}

Configuration explanation:

  • RegionID: Custom ID, recommended 900+ to avoid conflicts with official servers
  • RegionCode: Region code, customizable
  • RegionName: Display name, can use any language
  • HostName: Derper server domain
  • DERPPort: DERP service port (default 443)
  • STUNPort: STUN service port (default 3478)
  • OmitDefaultRegions: Whether to disable official servers (recommended to keep as backup)

Disable Official DERP Servers (Optional)#

If you want to use only your self-hosted server, you can disable official DERPs:

{
"derpMap": {
"OmitDefaultRegions": false,
"Regions": {
"900": {
// ... your custom server configuration ...
},
// Disable specific official servers
"1": null,
"2": null,
"3": null,
// ... other IDs ...
// Recommended to keep one as backup
// "20": null, # Hong Kong, as backup
}
}
}
WARNING

Not recommended to completely disable all official servers, in case your self-hosted server fails and Tailscale becomes completely unavailable.

Testing and Verification#

After configuration, test on any Tailscale-connected client:

Check Network Status#

Terminal window
tailscale netcheck

You should see output similar to:

Report:
* Time: 2025-12-30T02:39:55.29863832Z
* UDP: true
* IPv4: yes, ip:port
* IPv6: no, but OS has support
* MappingVariesByDestIP: false
* PortMapping:
* CaptivePortal: false
* Nearest DERP: catcat-derp
* DERP latency:
- sa: 43.2ms (catcat-derp)
- lax: 155.9ms (Los Angeles)
- sea: 165.5ms (Seattle)
- den: 169.7ms (Denver)
- sfo: 172.9ms (San Francisco)
- nue: 173.2ms (Nuremberg)
- dfw: 177.9ms (Dallas)
- hel: 182.8ms (Helsinki)
- hkg: 184.2ms (Hong Kong)
- hnl: 192.1ms (Honolulu)
- iad: 194.7ms (Ashburn)
- tor: 203.8ms (Toronto)
- ord: 206.5ms (Chicago)
- nyc: 207.6ms (New York City)
- mia: 216.1ms (Miami)
- tok: 216.2ms (Tokyo)
- par: 220.2ms (Paris)
- fra: 220.7ms (Frankfurt)
- lhr: 225.3ms (London)
- ams: 231.4ms (Amsterdam)
- waw: 237.6ms (Warsaw)
- mad: 238.4ms (Madrid)
- sin: 248.2ms (Singapore)
- syd: 291.3ms (Sydney)
- sao: 335.8ms (São Paulo)
- dbi: 340ms (Dubai)
- blr: 344.4ms (Bangalore)
- nai: 359.3ms (Nairobi)
- jnb: 385.1ms (Johannesburg)

Nearest DERP showing your self-hosted server name indicates successful configuration.

Test Connection Latency#

Terminal window
tailscale ping <device-name-or-ip>
tailscale netcheck
Terminal window
Report:
* Time: 2025-12-30T02:39:55.29863832Z
* UDP: true
* IPv4: yes, ip:port
* IPv6: no, but OS has support
* MappingVariesByDestIP: false
* PortMapping:
* CaptivePortal: false
* Nearest DERP: catcat-derp
* DERP latency:
- sa: 43.2ms (catcat-derp)
- lax: 155.9ms (Los Angeles)
- sea: 165.5ms (Seattle)
- den: 169.7ms (Denver)
- sfo: 172.9ms (San Francisco)
- nue: 173.2ms (Nuremberg)
- dfw: 177.9ms (Dallas)
- hel: 182.8ms (Helsinki)
- hkg: 184.2ms (Hong Kong)
- hnl: 192.1ms (Honolulu)
- iad: 194.7ms (Ashburn)
- tor: 203.8ms (Toronto)
- ord: 206.5ms (Chicago)
- nyc: 207.6ms (New York City)
- mia: 216.1ms (Miami)
- tok: 216.2ms (Tokyo)
- par: 220.2ms (Paris)
- fra: 220.7ms (Frankfurt)
- lhr: 225.3ms (London)
- ams: 231.4ms (Amsterdam)
- waw: 237.6ms (Warsaw)
- mad: 238.4ms (Madrid)
- sin: 248.2ms (Singapore)
- syd: 291.3ms (Sydney)
- sao: 335.8ms (São Paulo)
- dbi: 340ms (Dubai)
- blr: 344.4ms (Bangalore)
- nai: 359.3ms (Nairobi)
- jnb: 385.1ms (Johannesburg)

Look for your custom DERP server name (e.g., catcat-derp, depending on your naming) appearing at the top with the lowest latency, indicating traffic is relaying through your self-hosted Derper.

Check Server Logs#

Docker method:

Terminal window
docker logs -f derper

Manual compilation method:

Terminal window
sudo journalctl -u derper -f

Successful connection logs look like:

derper: accepting connection from 100.64.1.2:41234
derper: serving client 100.64.1.2

Troubleshooting#

1. Certificate Validation Failure#

Problem: Let’s Encrypt certificate request failed

Solutions:

  • Confirm domain is correctly resolved to server IP
  • Confirm firewall has opened port 80
  • Check if other services are using port 80
  • Review service logs for specific errors

2. Client Cannot Connect#

Problem: tailscale netcheck doesn’t show self-hosted server

Solutions:

  • Confirm Tailscale ACL configuration is saved
  • Wait a few minutes for configuration to take effect
  • Restart client Tailscale service
  • Check server firewall has correctly opened ports

3. DERP Verification Failure#

Problem: Cannot connect after enabling verify-clients

Solutions:

  • Confirm Tailscale client on server is started and joined network
  • Check if /var/run/tailscale/tailscaled.sock exists
  • For Docker, confirm socket file is correctly mounted
  • Review logs to confirm verification process

4. Cannot Connect After Using Custom Ports#

Problem: Service unavailable after modifying ports

Solutions:

  • Confirm firewall has opened corresponding ports
  • Confirm DERPPort and STUNPort in ACL are modified accordingly
  • Use telnet or nc to test port connectivity

Conclusion#

By self-hosting a Tailscale Derper server, you can effectively reduce relay latency and improve network experience. Docker deployment is simple and fast, suitable for quick setup; manual compilation offers more flexibility and control.

For production environments, it’s recommended to enable client verification and automatic certificate renewal to ensure service security and stability. Also, keep some official DERP servers as backup to avoid single point of failure causing network unavailability.

Deploy Tailscale Derper Self-Hosted Relay Server
https://catcat.blog/en/2025/12/deploy-tailscale-derper
作者
猫猫博客
发布于
2025-12-30
许可协议
CC BY-NC-SA 4.0