在 VPS 上部署自建 Tailscale DERP 节点(Docker Compose)

2025年8月8日 | Ruichen Zhou
TailscaleDERP自建服务网络

这篇文章是给未来的自己看的,也给需要自建 Tailscale DERP 的人做一个可复制的参考。

目标很简单: 在一台小型 VPS 上,用 Docker Compose 跑一个 derper 容器,支持:

  • Let’s Encrypt 自动签证书(80/443 开放时)
  • 或手动证书(80 被机房拦截时)
  • 在 Tailscale Admin Console 里通过 derpMap 管理单节点/多节点

0. 适用环境与整体思路

系统要求

  • Ubuntu 22.04+ 或 Debian 12+
  • 1C / 1G 也能跑(低负载场景足够)

依赖条件

  • 一台有公网 IPv4 的 VPS(建议开放 80/TCP、443/TCP、3478/UDP)
  • 一个域名,例如:derp.example.com,A 记录指向该 VPS
  • 一个已创建的 Tailscale Tailnet 账号
  • root 或可 sudo 的账号

推荐方案

  • 优先使用:Docker Compose + letsencrypt 自动证书
  • 若 80/TCP 无法使用,再切换到 manual + 自备证书

如果要跑多个 DERP 节点,建议为每台机器准备独立子域名,例如:

  • derp.example.com → Region hk1
  • derphk.example.com → Region hk2

1. 安装 Docker(含 Compose)

# 更新系统(可选)
sudo apt update && sudo apt -y upgrade

# 安装 Docker
sudo apt -y install docker.io

# 安装 docker compose 插件(Ubuntu 22.04+)
sudo apt -y install docker-compose-plugin

# 设置开机自启并立即启动
sudo systemctl enable --now docker

验证:

docker --version
docker compose version

2. 安装并登录 Tailscale(宿主机)

derper 如果启用 -verify-clients,需要访问宿主机的 tailscaled.sock, 因此宿主机必须运行 tailscaled 并加入你的 Tailnet。

curl -fsSL https://tailscale.com/install.sh | sh
sudo systemctl enable --now tailscaled
sudo tailscale up --ssh=false    # 按提示在浏览器登录

检查状态:

systemctl is-active tailscaled && tailscale status | head -n 5

3. 防火墙与安全组端口放行

需要同时在云厂商“安全组”和系统防火墙(如 UFW)中放行:

  • 80/TCP(Let’s Encrypt HTTP-01;如果后面只用 manual,可不开放)
  • 443/TCP(DERP TLS)
  • 3478/UDP(STUN)

示例(UFW):

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 3478/udp
sudo ufw enable

4. 目录规划与 Docker Compose

创建持久化目录和 compose 目录:

# 身份密钥 & ACME 缓存
sudo mkdir -p /opt/derper-data

# compose 项目
sudo mkdir -p /opt/derper && cd /opt/derper

新建 /opt/derper/docker-compose.yml记得替换域名):

services:
  derper:
    image: fredliang/derper:latest
    container_name: derper
    restart: unless-stopped
    ports:
      - "80:80/tcp"        # Let's Encrypt HTTP-01
      - "443:443/tcp"      # DERP TLS
      - "3478:3478/udp"    # STUN
    environment:
      DERP_DOMAIN: "derp.example.com"  # ← 改成你的域名
      DERP_ADDR: ":443"
      DERP_HTTP_PORT: "80"
      DERP_CERTMODE: "letsencrypt"     # 若 80/TCP 无法用,后面改成 manual
      DERP_STUN: "true"
      DERP_VERIFY_CLIENTS: "true"
    volumes:
      - /opt/derper-data:/var/lib/derper
      - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock

/var/lib/derper 需要持久化,里面包含:

  • derper.key(DERP 身份私钥)
  • ACME 缓存和证书 这样容器重建不会频繁重新签证书,也保持 DERP 身份稳定。

5. 启动容器与基础校验

启动:

cd /opt/derper
docker compose up -d

检查端口监听情况:

# 80 / 443
ss -lntup | egrep ':80|:443' || true
# 3478
ss -lnup | egrep ':3478' || true

查看日志:

docker compose logs --tail=120
# 预期能看到类似 “serving on :443 with TLS” 的输出

验证 TLS 证书是否正常(Issuer 是否为 Let’s Encrypt,CN/SAN 是否包含你的域名):

openssl s_client -connect derp.example.com:443 -servername derp.example.com -brief </dev/null

以上都正常,说明 DERP 服务端基本可用了。


6. 在 Tailscale Admin Console 中配置 derpMap

进入 Admin Console → Access Controls,在 ACL JSON 中添加 derpMap 字段。

控制台支持带注释 JSON(jsonc),如果提示语法错误,可以先去掉注释再保存。

6.1 单节点示例

"derpMap": {
  "OmitDefaultRegions": false,
  "Regions": {
    "901": {
      "RegionID": 901,
      "RegionCode": "hk1",
      "RegionName": "derp_1c1g",
      "Nodes": [
        {
          "Name": "1",
          "RegionID": 901,
          "HostName": "derp.example.com"
        }
      ]
    }
  }
}

6.2 双节点示例(新旧两台 VPS)

"derpMap": {
  "OmitDefaultRegions": false,
  "Regions": {
    "901": {
      "RegionID": 901,
      "RegionCode": "hk1",
      "RegionName": "derp_1c1g",
      "Nodes": [
        { "Name": "1", "RegionID": 901, "HostName": "derp.example.com" }
      ]
    },
    "902": {
      "RegionID": 902,
      "RegionCode": "hk2",
      "RegionName": "derp_2c4g",
      "Nodes": [
        { "Name": "1", "RegionID": 902, "HostName": "derphk.example.com" }
      ]
    }
  }
}

保存之后,一般 1–3 分钟内会下发到客户端。

客户端验证(任意一个节点):

tailscale netcheck
# 预期能看到 hk1 / hk2 的 Region,且有延迟(UDP=true 更好)

7. 无法使用 80/TCP 时:切换到 manual 证书模式

部分机房对 80/TCP 有限制,导致 Let’s Encrypt HTTP-01 失败。 这种情况下可以切换到 manual 模式,自行管理证书。

7.1 准备证书

准备好域名证书和私钥(CN/SAN 包含 derp.example.com):

  • derp.example.com.crt
  • derp.example.com.key

放到宿主机 /usr/local/cert

sudo mkdir -p /usr/local/cert
sudo cp derp.example.com.crt derp.example.com.key /usr/local/cert/
sudo chmod 700 /usr/local/cert
sudo chmod 600 /usr/local/cert/*

7.2 修改 Docker Compose

docker-compose.yml 中:

environment:
  DERP_CERTMODE: "manual"
  DERP_DOMAIN: "derp.example.com"
  DERP_ADDR: ":443"
  DERP_STUN: "true"
  DERP_VERIFY_CLIENTS: "true"
volumes:
  - /opt/derper-data:/var/lib/derper
  - /usr/local/cert:/cert            # ← 新增
  - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock

manual 模式下,镜像默认从 /cert 读取证书,一般不需要额外设置 DERP_CERTDIR。 这时可以不开放 80/TCP,但 443/TCP 和 3478/UDP 必须可达

重启并再次验证:

docker compose up -d
docker compose logs --tail=120
openssl s_client -connect derp.example.com:443 -servername derp.example.com -brief </dev/null

8. 常见问题与排查思路

1)443 端口占用

日志中出现 bind: address already in use

ss -lntup | egrep ':443'
# 找到占用进程,停止后再启动 derper 容器

2)tailscale netcheck 看不到自建 Region

  • 检查 Admin Console 中 derpMap 的 JSON 是否保存成功、无语法错误;
  • 等待 1–3 分钟或在客户端执行 tailscale up --reset 再登录;
  • 确认客户端网络能访问 DERP 域名的 443 端口。

3)UDP: false

  • 说明客户端所在网络的 UDP 打洞能力较差;
  • DERP 仍然可以工作,只是部分连接会走 TCP;
  • 如果能控制网络环境,尽量保证客户端网络出站UDP可用。

4)Let’s Encrypt 申请失败

在日志中看到 acme/autocert 相关错误,或关于 HostWhitelist 的警告时:

  • 检查域名解析是否正确指向当前 VPS;
  • 检查 80/TCP 是否可从外网访问;
  • 确认 DERP_DOMAIN 与访问时的域名 / SNI 一致;
  • 如果机房不放行 80/TCP,直接切 manual 模式即可。

5)-verify-clients 不生效

  • 宿主机必须运行 tailscaled
  • Compose 中需要挂载: - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
  • 可以用 tailscale status 确认宿主机已成功加入 Tailnet。

6)频繁重建容器触发 LE 频率限制

  • 确保 /var/lib/derper 已挂载到宿主机: - /opt/derper-data:/var/lib/derper
  • 尽量不要在测试阶段频繁清空该目录。

9. 迁移与备份

9.1 备份身份与 ACME 缓存

sudo tar -C /opt -czf derper-data-backup.tgz derper-data

9.2 迁移到新机器

  1. 在新机器上按前文步骤安装 Docker、Tailscale、防火墙规则;
  2. 将旧机器的 /opt/derper-data 拷贝到新机器同一路径;
  3. 把域名 A 记录切换到新 IP;
  4. 按前文步骤启动 compose 并验证。

10. 多节点模板(双节点示例)

假设:

  • 新 VPS(hk1):derp.example.com
  • 旧 VPS(hk2):derphk.example.com

两台机器可以使用相同的 docker-compose.yml 模板,只需要:

  • 修改 DERP_DOMAIN 为各自的域名;
  • 其余目录结构保持一致方便管理;

在 ACL 中使用前文的双节点 derpMap 配置,保存后,客户端执行:

tailscale netcheck

预期能看到 hk1 / hk2 的 Region 以及对应延迟。 连通性可以用:

tailscale ping <任意节点的 100.x IP>

11. 权限与安全建议

  • 持久化目录中包含私钥和证书,建议最小权限:

    sudo chown -R root:root /opt/derper-data /usr/local/cert
    sudo chmod 700 /opt/derper-data /usr/local/cert
    sudo find /usr/local/cert -type f -exec chmod 600 {} \;
  • 有需要的话,可以在宿主机额外部署:

    • fail2ban(简单暴力防护)
    • logrotate(管理日志体积)

日常排查优先看两处:

  • docker compose logs
  • 客户端的 tailscale netcheck

这篇文章的目的,是给自己留一份“从零到可用”的 DERP 部署手册。 后面如果需要扩容更多 Region,就按同样的模板复制一份 compose、换域名、在 derpMap 里加 Region 即可。