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.
Method 1: Docker Deployment (Recommended)
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:
# Install Docker (Ubuntu example)curl -fsSL https://get.docker.com | bash
# Start Docker servicesudo systemctl enable dockersudo systemctl start dockerCreate Docker Compose Configuration
Create a working directory and write docker-compose.yml:
mkdir -p /opt/derpercd /opt/derperCreate 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/certsEnvironment Variables
| Variable | Required | Description | Default |
|---|---|---|---|
DERP_DOMAIN | Yes | DERP server domain name | your-hostname.com |
DERP_CERT_DIR | No | Certificate storage directory | /app/certs |
DERP_CERT_MODE | No | Certificate mode: manual or letsencrypt (auto) | letsencrypt |
DERP_ADDR | No | DERP service listen address | :443 |
DERP_STUN | No | Enable STUN service | true |
DERP_STUN_PORT | No | STUN service port | 3478 |
DERP_HTTP_PORT | No | HTTP service port, set to -1 to disable | 80 |
DERP_VERIFY_CLIENTS | No | Verify client identity via local Tailscale client | false |
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/certsNOTEWhen using custom ports, you’ll need to modify
DERPPortandSTUNPortin 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):
# Download Golang (1.22.0 example)wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
# Extract to /usr/localsudo rm -rf /usr/local/gosudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
# Configure environment variablesecho 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrcsource ~/.bashrc
# Verify installationgo versionCompile Derper
# Clone Tailscale repositorygit clone https://github.com/tailscale/tailscale.gitcd tailscale
# Compile derpergo build cmd/derper/derper.go
# Move to system pathsudo mv derper /usr/sbin/derperCreate Systemd Service
Create service file /etc/systemd/system/derper.service:
[Unit]Description=Tailscale DerperWants=network-pre.targetAfter=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-clientsRestart=on-failureRestartSec=5
[Install]WantedBy=multi-user.targetParameter explanation:
--hostname: Your domain name-a: DERP service listen address-http-port: HTTP port for Let’s Encrypt certificate validation-certmode: Certificate mode,letsencryptfor auto,manualfor manual management--certdir: Certificate storage directory-verify-clients: Enable client verification (prevents unauthorized usage)
Start Service
# Create certificate directorysudo mkdir -p /var/lib/derper/certs
# Reload systemd configurationsudo systemctl daemon-reload
# Start and enable auto-startsudo systemctl enable derpersudo systemctl start derper
# Check service statussudo systemctl status derperCertificate 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=80Manual compilation method:
ExecStart=/usr/sbin/derper \ --hostname=derper.example.com \ -a :443 \ -http-port 80 \ -certmode letsencrypt \ --certdir /var/lib/derper/certsManual 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:
# Install Tailscalecurl -fsSL https://tailscale.com/install.sh | sh
# Start and join networksudo tailscale upEnable Verification
Docker method:
Ensure docker-compose.yml includes:
environment: - DERP_VERIFY_CLIENTS=truevolumes: - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sockManual 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-clientsRestart service:
sudo systemctl daemon-reloadsudo systemctl restart derperConfigure Tailscale
Modify Access Controls
- Visit Tailscale Admin Console
- 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 serversRegionCode: Region code, customizableRegionName: Display name, can use any languageHostName: Derper server domainDERPPort: 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 } }}WARNINGNot 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
tailscale netcheckYou 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
tailscale ping <device-name-or-ip>tailscale netcheckReport: * 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:
docker logs -f derperManual compilation method:
sudo journalctl -u derper -fSuccessful connection logs look like:
derper: accepting connection from 100.64.1.2:41234derper: serving client 100.64.1.2Troubleshooting
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.sockexists - 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
DERPPortandSTUNPortin ACL are modified accordingly - Use
telnetorncto 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.