代码学习极客

返回 · 2026-06-11T07:02:00+00:00

Headscale + DERP 自建结点:低带宽服务器跑控制平面,高带宽服务器跑流量

Headscale + DERP 自建结点:低带宽服务器跑控制平面,高带宽服务器跑流量

楔子

上次折腾 Tailscale 的时候,被一个问题卡了很久:我的小鸡(国内 NAT 机器)带宽实在可怜,跑官方 DERP 结点慢得像便秘,但换成自建 DERP 结点后分流效果立竿见影。后来研究了一下 Headscale + DERP 的组合,发现可以把这个架构拆开——Headscale 控制平面跑在低带宽服务器上,只做信令交互;DERP 结点跑在大带宽服务器上,专门扛流量。这篇文章就是踩坑记录。

原理简单说

Tailscale 的流量有两种走法:

  1. 直接 P2P:两台机器在同一个 NAT 后面或者能直连,走直连,延迟最低
  2. DERP 中转:当 P2P 打不通的时候(比如对称 NAT、端口封锁等),流量走 DERP 结点做转发

官方 DERP 结点延迟感人,尤其是国内鸡——绕一圈出去再回来,带宽还被抽了一刀。自建 DERP 的好处是你可以选节点位置,让流量走最优路径。

Headscale 是 Tailscale 控制平面的开源替代品,支持自建 DERP 结点,而且可以把 Headscale 和 DERP 分开部署在不同的机器上。这就很舒服了:

  • 小带宽机器:跑 Headscale 控制平面,做信令交换、节点管理、ACL 控制
  • 大带宽机器:跑 DERP 结点,走流量,延迟低、带宽足

架构图

┌─────────────────────┐         ┌─────────────────────┐
│  小带宽服务器        │         │  大带宽服务器        │
│  (国内 NAT 机)       │         │  (有独立 IP 的鸡)    │
│                     │         │                     │
│  Headscale 控制平面  │◄───────►│  DERP 中转结点       │
│  (:443 TLS)         │  信令   │  (:3478 UDP/TCP)     │
└─────────────────────┘         └─────────────────────┘
         ↑                               ↑
         │                               │
    Tailscale 客户端                 Tailscale 客户端
         │                               │
    手机/电脑/其他节点              手机/电脑/其他节点

第一步:Headscale 控制平面(小带宽机器)

环境

  • 系统:Debian 12
  • 配置:1C1G 小鸡足够了,信令交换不吃配置
  • 域名:hs.whcself.xyz(已备案)

安装 Headscale

# 下载二进制
wget https://github.com/juanfont/headscale/releases/download/v0.23.0/headscale_0.23.0_linux_amd64
sudo mv headscale_0.23.0_linux_amd64 /usr/local/bin/headscale
sudo chmod +x /usr/local/bin/headscale

# 创建用户
sudo useradd -m -s /bin/bash headscale

# 创建配置目录
sudo mkdir -p /etc/headscale
sudo mkdir -p /var/lib/headscale

配置文件

sudo nano /etc/headscale/config.yaml
server_url: https://hs.whcself.xyz
listen_addr: 0.0.0.0:443
metrics_listen_addr: 127.0.0.1:9090

db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite

acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: "your-email@example.com"
tls_letsencrypt_hostname: "hs.whcself.xyz"

prefixes:
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48
  allocation: sequential

derp:
  server:
    enabled: false   # 我们用外置 DERP,这儿关掉
  urls:
    - https://hs.whcself.xyz/derp

log:
  format: text
  level: info

注意:这里 DERP server 部分 enabled: false,因为我们要单独部署 DERP 结点。urls 里填的是 DERP 结点的 URL,后面会用到。

Nginx 反向代理(处理 TLS)

sudo apt install nginx certbot python3-certbot-nginx -y
sudo nano /etc/nginx/sites-available/headscale
server {
    listen 80;
    server_name hs.whcself.xyz;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name hs.whcself.xyz;

    ssl_certificate /etc/letsencrypt/live/hs.whcself.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hs.whcself.xyz/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:443;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /derp {
        proxy_pass http://127.0.0.1:443;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
sudo certbot --nginx -d hs.whcself.xyz
sudo systemctl enable --now nginx

启动 Headscale

sudo systemctl edit headscale.service
[Unit]
Description=Headscale control plane
After=network.target

[Service]
ExecStart=/usr/local/bin/headscale serve
Restart=always
User=headscale
Group=headscale

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now headscale

创建 namespace 和预共享密钥

# 创建命名空间(相当于 Tailscale 的 tailnet)
headscale namespace create myblog

# 创建预共享密钥(给客户端用)
headscale preauthkey create -n myblog --reusable --expiration 2030-01-01T00:00:00Z
# 记住这个 key,客户端要用

第二步:DERP 结点(大带宽机器)

环境

  • 系统:Debian 12
  • 配置:2C4G 或者更好,带宽要足
  • 域名:derp.whcself.xyz(复用备案域名,子域名指向 DERP)
  • 端口:3478 UDP/TCP(DERP 通信)、443(Health check)

安装 DERP

Tailscale 官方提供了独立的 DERP 二进制,不需要装完整的 Tailscale:

wget https://github.com/juanfont/headscale/releases/download/v0.23.0/headscale_0.23.0_linux_amd64
sudo mv headscale_0.23.0_linux_amd64 /usr/local/bin/headscale-derp
sudo chmod +x /usr/local/bin/headscale-derp

DERP 配置文件

sudo mkdir -p /etc/headscale-derp
sudo nano /etc/headscale-derp/config.yaml
listen: 0.0.0.0:3478

privateKeyPath: /etc/headscale-derp/private.key
publicKeyPath: /etc/headscale-derp/public.key

verifyClients: true
backendUrl: https://hs.whcself.xyz

生成密钥对

cd /etc/headscale-derp
sudo /usr/local/bin/headscale-derp keys generate
# 会生成 private.key 和 public.key

Nginx + TLS

DERP 的 WebSocket 健康检查走 443:

sudo nano /etc/nginx/sites-available/derp
server {
    listen 80;
    server_name derp.whcself.xyz;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name derp.whcself.xyz;

    ssl_certificate /etc/letsencrypt/live/derp.whcself.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/derp.whcself.xyz/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3478;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
sudo certbot --nginx -d derp.whcself.xyz
sudo systemctl enable --now nginx

启动 DERP

sudo useradd -m -s /bin/bash headscale-derp
sudo chown -R headscale-derp:headscale-derp /etc/headscale-derp

sudo nano /etc/systemd/system/headscale-derp.service
[Unit]
Description=Headscale DERP relay node
After=network.target

[Service]
ExecStart=/usr/local/bin/headscale-derp run -c /etc/headscale-derp/config.yaml
Restart=always
User=headscale-derp
Group=headscale-derp

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now headscale-derp

第三步:在 Headscale 注册 DERP 结点

回到小带宽服务器,执行:

# 获取 DERP 结点的公钥
cat /etc/headscale-derp/public.key
# 假设是: derp1234567890abcdef...

# 注册到 Headscale
headscale derp register --server-name derp-whcself --public-key derp1234567890abcdef... --hostname derp.whcself.xyz

验证一下:

headscale derp list

第四步:客户端接入

Linux

# 安装 Tailscale(不是 Headscale,是 Tailscale 客户端)
curl -fsSL https://tailscale.com/install.sh | sh

# 使用预共享密钥接入(不暴露用户名密码)
sudo tailscale up --login-server https://hs.whcself.xyz --authkey tskey-auth-key-reusable-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

手机(Android/iOS)

下载 Tailscale 应用,登录方式选 Use another login server,填入 https://hs.whcself.xyz,然后用预共享密钥认证。

踩坑记录

坑一:SSL 证书 chain 不完整

DERP 客户端验证 Headscale 控制平面时,如果证书 chain 不完整,会报错 context deadline exceeded。确保 Let's Encrypt 证书包含完整 chain,或者在 Headscale 配置里指定 tls_letsencrypt_challenge_port: 80

坑二:复用备案域名的子域名指向问题

备案是针对主域名的,子域名如果指向不同 IP,理论上不算新备案(工信部看的是主域名)。但要注意:
- 同一个备案主体下的子域名指向不同服务器是允许的
- 如果子域名指向其他服务器,部分服务商可能会拦截,需要确保域名已完成ICP备案
- SSL 证书正常申请就行,Let's Encrypt 不管你备案不备案

坑三:NAT 机访问 DERP 延迟高

如果客户端也在 NAT 后面,第一次连接 DERP 可能需要打洞。Headscale 支持 STUN 来检测 NAT 类型:

headscale nodes list  # 查看节点列表,确认 DERP 结点注册成功

如果所有节点都显示 offline,检查一下 DERP 结点是否在 3478 端口监听:

ss -ulnp | grep 3478

坑四:DERP 健康检查

DERP 有个 /健康检查 端点,可以用这个命令验证:

curl https://derp.whcself.xyz/health

正常返回 {"healthy": true}

结尾

这套架构跑下来,小带宽服务器只负责信令,几乎不占流量;大带宽服务器专门走流量,带宽利用率直接拉满。备案域名复用 + Let's Encrypt 自动续期,维护成本几乎为零。

如果你的大带宽鸡延迟比官方 DERP 低很多,尤其适合打游戏或者视频通话场景。

有什么问题欢迎留言。