概述 Tailscale 基于用户态 WireGuard 协议构建,通过三层穿透策略实现高效组网,在所有节点间流量通过 WireGuard 实现高性能的端到端加密隧道。但是控制端是闭源的,子网无法自定义,免费版只能使用 20 台设备。这些都能忍受,因为个人使用完全足以。最大的缺点是官方中继 DERP 服务器全都在海外,之前个人使用时,还能稳定在 50ms 左右,但是最近延迟突然变得很大,于是需要找到了开源替代解决方案——Headscale。
Headscale 是 Tailscale 的开源控制平面,复刻 Tailscale 控制服务器逻辑,支持无限设备、私有化部署、自定义子网段。非常适合个人使用或小型开源组织。
DERP 是 Tailscale 自研的一个协议,但是官方中继 DERP 服务器全都在海外,为了实现低延迟、高安全性,我们可以参考 Tailscale 官方文档 自建私有的 DERP 服务器。官方提供的部署方法,默认仅支持基于域名访问,对于国内的服务器,域名需要备案。
本文会介绍如何自建 Headsale 实现控制平面,并基于公有云服务器实现自定义 DERP 服务器。
先决条件
带有公网 IP 的主机
可以公网解析的域名
权威证书
headscale 安装 常规安装方法 headscale 该方法需要有可以公网解析的域名,同时如果服务器在国内的话,域名需要备案。
从官方 Release 页面,根据你的操作系统选择对应的安装包,例如我的是 Ubuntu 系统,所以下载 headscale_0.26.1_linux_amd64.deb
这个包。
1 2 wget https://github.com/juanfont/headscale/releases/download/v0.26.1/headscale_0.26.1_linux_amd64.deb -O headscale.deb apt install ./headscale.deb
通过这种方式,可以自动初始化 config.yaml
和 headscale.service
这两个配置文件。并自动创建对应的用户和用户组。
配置/etc/headscale/config.yaml
,这里需要关注几个配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ## 访问的域名 server_url: https://headscale.zerchin.xyz ## API 监听端口 listen_addr: 0.0.0.0:8080 ## grpc_listen_addr: 0.0.0.0:50443 ## 根据查询到的资料,建议打开随机端口,否则可能会出现连接不稳定的问题 randomize_client_port: true ## 自定义子网,如果跟现有网段不冲突,可以不修改 prefixes: v4: 100.64.0.0/10
启动 headscale 服务
1 systemctl enable --now headscale
检查 headscale 服务状态
1 2 systemctl status headscale journalctl -xef -u headscale
headscale-ui 默认 headscale 不提供 Web UI 界面,但是社区 提供了很多实现 UI 的工具,例如这里可以使用 Headscale-ui 为 headscale 提供 Web UI 展示。
需要安装 docker 环境
1 docker run -itd -p 3000:8080 --name headscale-ui ghcr.io/gurucomputing/headscale-ui:latest
安装好之后,访问 http://<your_IP>:3000/web
如果使用的是 headscale-admin,则访问的地址是 /admin
此时 UI 还对接不上 headscale,会有跨域问题,所以要做个反向代理把他俩串起来。
nginx 社区有很多种反向代理的实现方案,例如使用 caddy、traefik、nginx 等实现反向代理。这里使用我最熟悉的 nginx 来实现。
具体参考配置如下:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 worker_processes auto;worker_rlimit_nofile 40000 ;events { worker_connections 8192 ; } http { map $http_upgrade $connection_upgrade { default Upgrade; '' close; } server { listen 443 ssl http2; server_name headscale.zerchin.xyz; ssl_certificate /etc/nginx/ssl/tls.crt; ssl_certificate_key /etc/nginx/ssl/tls.key; ssl_protocols TLSv1.2 TLSv1.3 ; location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $server_name; proxy_redirect http:// https://; proxy_buffering off ; 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; add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; } location /web/ { proxy_pass http://127.0.0.1:3000/web; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $server_name; proxy_redirect http:// https://; proxy_buffering off ; 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; add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; } } server { listen 80 ; server_name headscale.zerchin.xyz; return 301 https://$host$request_uri; } }
这里需要修改这几个地方:
server_name
:修改为访问的域名名称,不要带 http/https。
proxy_pass
:这里填写 headscale 和 headscale-ui 的地址,如果前面按照我的步骤操作,则不用修改。
将上述文件保存到/root/headscale/nginx/
路径下。
接着我们要准备好域名的权威证书,并将域名的权威证书放到 /root/headscale/nginx/ssl
路径下,并重命名为 tls.crt
和 tls.key
。
最后通过 docker 启动 nginx 服务。
1 2 3 4 5 6 docker run -itd \ --name headscale-proxy \ -p 80:80 -p 443:443 \ -v /root/headscale/nginx/ssl:/etc/nginx/ssl \ -v /root/headscale/nginx/nginx.conf:/etc/nginx/nginx.conf \ nginx
验证 首先我们需要创建一个 key
1 headscale apikeys create
接着访问 UI:http://<your_domain>/web
,在 Settings下填写配置。
其中:
Headscale URL 填写 https://<your_domain>
Headscale API Key 填写 前面刚刚创建的 apikey
接着点击 Test Server Settings ,当旁边出现绿色的√ 时,说明对接成功,我们可以通过 UI 管理 User、Device。
Docker Compose 安装方法 前面我们在安装 headscale-ui 和 nginx 时,我们使用了 docker 去启动。实际上,headscale 本身也有 docker 镜像,所以我们可以直接通过 docker compose 一键启动所有的服务。
首先新建一个存放配置的目录
1 2 3 4 cd ~ mkdir headscale cd headscale mkdir -p {nginx,nginx/ssl,config,lib,run}
接着在 nginx 目录下,配置 nginx.conf
文件,在 nginx/ssl 目录下,存放证书文件,如下:
1 2 3 4 5 # ls nginx/* nginx/nginx.conf nginx/ssl: tls.crt tls.key
nginx.conf
参考写法稍微有些不同,直接复制下面这个配置,修改一下 server_name
即可使用:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 worker_processes auto;worker_rlimit_nofile 40000 ;events { worker_connections 8192 ; } http { map $http_upgrade $connection_upgrade { default Upgrade; '' close; } server { listen 443 ssl http2; server_name headscale.zerchin.xyz; ssl_certificate /etc/nginx/ssl/tls.crt; ssl_certificate_key /etc/nginx/ssl/tls.key; ssl_protocols TLSv1.2 TLSv1.3 ; location / { proxy_pass http://headscale:8080; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $server_name; proxy_redirect http:// https://; proxy_buffering off ; 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; add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; } location /web/ { proxy_pass http://headscale-ui:8080/web; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $server_name; proxy_redirect http:// https://; proxy_buffering off ; 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; add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; } } server { listen 80 ; server_name headscale.zerchin.xyz; return 301 https://$host$request_uri; } }
配置 docker-compose.yml
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 27 28 29 30 31 32 services: headscale: image: docker.io/headscale/headscale:v0.26.1-debug restart: unless-stopped container_name: headscale ports: - "8080:8080" - "9090:9090" volumes: - ./config:/etc/headscale - ./lib:/var/lib/headscale - ./run:/var/run/headscale command: serve headscale-ui: image: ghcr.io/gurucomputing/headscale-ui:latest restart: unless-stopped container_name: headscale-ui ports: - "3000:8080" headscale-proxy: image: nginx restart: unless-stopped container_name: headscale-proxy ports: - "443:443" - "80:80" volumes: - "./nginx/nginx.conf:/etc/nginx/nginx.conf" - "./nginx/ssl:/etc/nginx/ssl" depends_on: - headscale - headscale-ui
这里 headsacle 使用了-debug的镜像,会自带一些基础工具,否则连 sh、ls 这些命令都没有
最后直接一键启动。
安装好之后,apikey 通过 docker 命令获取。
1 docker exec -it headscale headscale apikey create
http 安装方法 如果没有域名,则无法通过上述方法进行安装,此时我们可以使用 http 方式安装。
需要修改这几个地方:
headscale 的 config.yaml
文件里,修改 server_url: http://xx.xx.xx.xx
nginx.conf
配置需要进行修改,参考如下,可以直接修改其中的 server_name
为自己的 IP 直接使用:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 worker_processes auto;worker_rlimit_nofile 40000 ;events { worker_connections 8192 ; } http {map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80 ; server_name 192.168.2.51 ; location / { proxy_pass http://headscale:8080; proxy_set_header Host $server_name; 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; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_buffering off ; add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; proxy_connect_timeout 60s ; proxy_send_timeout 60s ; proxy_read_timeout 60s ; proxy_buffer_size 64k ; proxy_buffers 16 32k ; proxy_busy_buffers_size 64k ; proxy_temp_file_write_size 64k ; } location /web/ { proxy_pass http://headscale-ui:8080/web; proxy_set_header Host $server_name; 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; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_buffering off ; add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; proxy_connect_timeout 60s ; proxy_send_timeout 60s ; proxy_read_timeout 60s ; proxy_buffer_size 64k ; proxy_buffers 16 32k ; proxy_busy_buffers_size 64k ; proxy_temp_file_write_size 64k ; } } }
docker-compose.yml
中把 tls 相关的配置拿掉
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 27 28 29 30 services: headscale: image: docker.io/headscale/headscale:v0.26.1-debug restart: unless-stopped container_name: headscale ports: - "8080:8080" - "9090:9090" volumes: - ./config:/etc/headscale - ./lib:/var/lib/headscale - ./run:/var/run/headscale command: serve headscale-ui: image: ghcr.io/gurucomputing/headscale-ui:latest restart: unless-stopped container_name: headscale-ui ports: - "3000:8080" headscale-proxy: image: nginx restart: unless-stopped container_name: headscale-proxy ports: - "80:80" volumes: - "./nginx/nginx.conf:/etc/nginx/nginx.conf" depends_on: - headscale - headscale-ui
最后执行docker-compose up -d
启动,效果如下:
创建用户 在 Headscale 中,一个用户管理着多个节点(设备),用户与用户之间是隔离的。节点始终只会归属于一个用户,在一个用户下的节点网络是互通的。
首先我们需要创建一个用户:
1 headscale user create user01
创建 preauthkeys,在 Linux 客户端需要用到,保存备用:
1 headscale preauthkeys create -u 1 --reusable -e 1d
这里官方文档说明中,-u 后面跟的是 user name,但是实际上需要填写 user id
user 和 preauthkeys 也可以通过 UI 创建。
客户端节点注册 客户端是使用原生的 tailscale 工具。
目前几乎所有的系统都能支持运行,包括 主流 Linux 操作系统,Windows、Mac、IOS、Android。
这里以 Ubuntu 和 Mac 为例。其他系统也是类似,具体细节可以参考 headscale 官方文档 。
Ubuntu 配置必要的内核参数:
1 2 3 echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
首先要安装 tailscale 工具 ,官方提供了一键安装的脚本,我们可以直接执行:
1 curl -fsSL https://tailscale.com/install.sh | sh
这条命令会安装 tailscale 工具,并启动 tailscaled 服务。安装好之后,执行命令确认该服务是否是 running 状态。
1 systemctl status tailscaled
确认 tailscale0 网络设备已创建出来。
1 2 3 4 5 3: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc fq_codel state UNKNOWN group default qlen 500 link/none inet6 fe80::d06a:c117:207f:648f/64 scope link stable-privacy valid_lft forever preferred_lft forever
接下来就可以通过 tailscale 接入到 headscale。
方法1:(Ubuntu 请直接参考方法 2,其他系统可以参考此方法)
直接在节点上执行注册命令:
1 tailscale up --login-server https://headscale.zerchin.xyz --accept-dns=false --accept-routes --advertise-routes 192.168.2.0/24
然后会输出一个地址得到个 key,在 Server 上执行命令注册:
1 headscale nodes register --user <USER> --key <YOUR_MACHINE_KEY>
这个是官方给的第一个连接的方法,但是这里我试过有问题,所以直接看第二个方法。
方法 2:使用前面我们获取到的 preauthkey 注册节点:
1 tailscale up --login-server https://headscale.zerchin.xyz --authkey 7c6601397c9e0c06fc28743134e24bf6f851eee0cf65af88 --accept-dns=false --accept-routes=true --advertise-routes=192.168.2.0/24
主要参数说明:
authkey
:已授权的 key,使用该 key 直接就能注册客户端节点,不需要 Headscale 手动同意。
accept-dns
:是否接受 DNS 配置,默认是 true。
accept-routes
:是否接受其他节点通告的路由,默认是 false。
advertise-routes
:宣告给其他节点的路由,如果有多条,则以逗号分隔,例如"10.0.0.0/8,192.168.0.0/24"
。
查看节点列表,可以看到已经注册上来并且是 online 状态。
1 2 3 ID | Hostname | Name | MachineKey | NodeKey | User | IP addresses | Ephemeral | Last seen | Expiration | Connected | Expired 1 | hs-31 | hs-31 | [1SQ6S] | [cP7Bw] | user01 | 100.64.0.1, fd7a:115c:a1e0::1 | false | 2025-06-08 08:08:43 | N/A | online | no
查看路由列表:
1 2 3 ID | Hostname | Approved | Available | Serving (Primary) 1 | hs-31 | | 192.168.2.0/24 |
这里路由只是告知给了 headscale,还需要通过 Headscale 手动同意宣告该路由才行。
确认路由已被宣告:
1 2 3 ID | Hostname | Approved | Available | Serving (Primary) 1 | hs-31 | 192.168.2.0/24 | 192.168.2.0/24 | 192.168.2.0/24
Mac 按住 option 键点击 tailscale 图标,会出现 Debug 选项,选择 Add Account,填写 headscale Server 地址:https://headscale.zerchin.xyz
接着浏览器会弹出一个窗口,该窗口包含注册节点的命令(其实就是上一步的方法一),如下:
将这个命令复制下来,到 headscale 的主机上运行:
1 headscale nodes register --user user01 --key hobweC-xxgC-uzgjdZF4GkqI
这里 –user 要使用名字而不是 id
查看节点列表状态
1 2 3 4 ID | Hostname | Name | MachineKey | NodeKey | User | IP addresses | Ephemeral | Last seen | Expiration | Connected | Expired 1 | hs-31 | hs-31 | [1SQ6S] | [cP7Bw] | user01 | 100.64.0.1, fd7a:115c:a1e0::1 | false | 2025-06-08 08:12:43 | N/A | online | no 2 | Zeqins-MacBook-Pro | zeqins-macbook-pro | [yvja1] | [blV3O] | user01 | 100.64.0.3, fd7a:115c:a1e0::3 | false | 2025-06-08 08:12:45 | N/A | online | no
DERP DERP 全称是 Designated Encrypted Relay for Packets,是 Tailscale 网络中的一种加密中继服务器。DERP 中继仅充当“盲转发器”,不会解密流量,因为数据在设备端已全程加密,私钥不离设备。作用主要有两点:
Tailscale 在全球多个区域都部署了 DERP 服务器,客户端启动时会从控制端获取包含 DERP 服务器位置及延迟信息的 “DERP map”,并通过 netcheck 选择“家乡”节点,控制平面会把该信息告知其它设备。在实际连接中,设备 A 会先通过 B 正在使用的 DERP 中继尝试建立直接连接,如果成功则转为 UDP 直连,否则一直走中继流量。
但是 DERP 在中国大陆没有节点,所以可能会导致一旦流量通过 DERP 服务器进行中继,延时就会非常高。不过不用担心,官方提供了自建 DERP 服务器 的方法。我们可以使用这种方法自建 DERP 服务器,可以基于域名 访问,也可以自行魔改使用 IP 访问。这两种方法各有利弊。
这里需要自行权衡利弊。下面会介绍如何启用这两种方法。
源码编译
准备 golang 环境
1 2 3 4 5 6 7 8 9 apt install -y wget git openssl curl wget https://golang.google.cn/dl/go1.24.3.linux-amd64.tar.gz rm -rf /usr/local /go && tar -C /usr/local -xzf go1.24.3.linux-amd64.tar.gz export PATH=$PATH :/usr/local /go/bingo version echo "export PATH=$PATH :/usr/local/go/bin" >> /etc/profilesource /etc/profile
下载 DERP 源代码
1 2 3 4 5 go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct go install tailscale.com/cmd/derper@main cp /root/go/bin/derper /usr/local /bin/derper
导入证书
创建证书目录,并将证书上传到这里,证书命名规则是<domian_name>.key
、<domian_name>.crt
:
配置 systemd
1 2 3 4 5 6 7 8 9 10 11 12 cat /etc/systemd/system/derp.service [Unit] Description=TS Derper After=network.target Wants=network.target [Service] User=root Restart=always ExecStart=/usr/local /bin/derper -hostname <domain_name> -a :33445 -http-port 33446 -certmode manual -certdir /etc/derp RestartPreventExitStatus=1 [Install] WantedBy=multi-user.target
启动 derp 服务
1 systemctl enable --now derp
检查
1 journalctl -xef -u derp.service
防火墙需要放行 HTTPS 33445 和 STUN 3478 端口
纯 IP 方式 需要重新编译源码文件cert.go
,编辑 文件,注释这个方法的前三行。
1 2 cd ~/go/pkg/mod/[tailscale]/cmd/derpervi cert.go
1 2 3 4 5 6 7 8 9 10 11 12 func (m *manualCertManager) getCertificate (hi *tls.ClientHelloInfo) (*tls.Certificate, error) { certCopy := new (tls.Certificate) *certCopy = *m.cert certCopy.Certificate = certCopy.Certificate[:len (certCopy.Certificate):len (certCopy.Certificate)] return certCopy, nil }
重新编译:
1 go build -o /usr/local /bin/derper
准备自签名证书:
1 openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout /etc/derp/derp.myself.com.key -out /etc/derp/derp.myself.com.crt -subj "/CN=derp.myself.com" -addext "subjectAltName=DNS:derp.myself.com"
设置 systemd:
1 2 3 4 5 6 7 8 9 10 11 12 cat /etc/systemd/system/derp.service [Unit] Description=TS Derper After=network.target Wants=network.target [Service] User=root Restart=always ExecStart=/usr/local /bin/derper -hostname derp.myself.com -a :33445 -http-port 33446 -certmode manual -certdir /etc/derp --verify-clients=false RestartPreventExitStatus=1 [Install] WantedBy=multi-user.target
启动 derp 服务:
1 systemctl enable --now derp
对接 Headscale
准备好 derp.json
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { "Regions" : { "901" : { "RegionID" : 901 , "RegionCode" : "custom-derp-server" , "RegionName" : "Custom Derp Server" , "Nodes" : [ { "Name" : "901a" , "RegionID" : 901 , "DERPPort" : 33445 , "HostName" : "xxxx" , "IPv4" : "xxxx" , "InsecureForTests" : true } ] } } }
主要参数说明:
RegionID
:每个区域都有一个唯一的 ID。ID 值 900 到 999 保留用于自定义用户指定的区域,Tailscale 不会使用他们。
HostName
:DERP 服务器的域名地址。
IPv4
:如果是纯 IP 方式,则要填写 IP 地址。
InsecureForTests
:跳过域名验证。
我们需要让 Headscale 读到这个配置文件,但是 Headscale 只能够通过 URL 的形式读取,所以我们可以自己弄一个 nginx 容器,参考如下:
1 2 3 4 docker run -itd --name derp-json -p 8081:80 -v ${PWD} /derp.json:/usr/share/nginx/html/derp.json nginx curl localhost:8081/derp.json
接着编辑 headscale 的 config.yaml
文件,将原本的官方的 derpmap 禁用掉,使用自己的 derp.json
,修改位置如下:
1 2 3 4 derp: urls: - http://<derp-json_server_ip>:8081/derp.json
最后重启一下 headscale 服务(如果是 docker 运行就重启容器)
1 2 3 systemctl restart headscale docker restart headscale
检查是否使用自己的 DERP 服务器。
1 2 3 4 5 6 7 ... ... ... * Nearest DERP: Custom Derp Server * DERP latency: - custom-derp-server: 2.8ms (Custom Derp Server)
查看延迟
1 2 pong from macbook-pro (100.64.198.12) via 27.38.171.20:35657 in 13ms
测了下速度,还是很可观的。
对接官方 Tailscale 自己准备的 DERP 服务器,也可以用于 Tailscale ,只需要在 Tailscale console - Access Control 下,添加如下配置:
如果是域名方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 "derpMap": { "OmitDefaultRegions": true, "Regions": { "900": { "RegionID": 900, "RegionCode": "custom-derp-server", "RegionName": "Custom Derp Server", "Nodes": [ { "Name" : "900a" , "RegionID" : 900 , "DERPPort" : 33445 , "HostName" : "<your_domain_name>" , }, ], }, }, },
如果是纯 IP 方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 "derpMap": { "OmitDefaultRegions": true, "Regions": { "900": { "RegionID": 900, "RegionCode": "custom-derp-server", "RegionName": "Custom Derp Server", "Nodes": [ { "Name" : "900a" , "RegionID" : 900 , "DERPPort" : 33445 , "IPv4" : "<ip_addr>" , "InsecureForTests" : true , }, ], }, }, },
主要参数说明:
OmitDefaultRegions
:使用自己的 DERP 服务器而不使用官方的。
保存之后,在 Machines 页面下,点击任意一个客户端,可以看到是否选择了自定义的 DERP 服务器,看上去效果很不错。
构建 Docker 镜像 这里一步步安装还是太麻烦了点,那么我们是否可以自己构建 DERP 镜像,而不用每次都手动编译呢?答案是肯定的。
可以基于上述的步骤,自行构建 Docker 镜像,以后就可以直接在每个节点直接运行该镜像,快速拉起一个 DERP 服务。
这里我已经构建好镜像并测试验证通过,可放心食用。
1 docker run -itd --name derper --net host zerchin/derper-ip:v0.1
如果不放心的话,这里也放出来构建的过程,自行参考下面。
Dockerfile
文件参考如下:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 FROM golang:latest AS builder WORKDIR /app RUN git clone https://github.com/tailscale/tailscale.git RUN sed -i '/if hi\.ServerName != m\.hostname && !m\.noHostname {/,/}/ { s/^/\/\//; s/^/\/\//; s/^/\/\//; }' /app/tailscale/cmd/derper/cert.go RUN cd /app/tailscale/cmd/derper/ && CGO_ENABLED=0 GOTOOLCHAIN=auto /usr/local/go/bin/go build -buildvcs=false -ldflags "-s -w" -o /app/derper FROM ubuntu:24.04 WORKDIR /app ENV DERP_ADDR :443 ENV DERP_HTTP_PORT 80 ENV DERP_HOST=127.0.0.1 ENV DERP_CERTS=/app/certs/ ENV DERP_STUN true ENV DERP_VERIFY_CLIENTS false RUN apt-get update && \ apt-get install -y openssl curl COPY --from=builder /app/derper /app/derper COPY build_cert.sh /app/build_cert.sh CMD bash /app/build_cert.sh $DERP_HOST $DERP_CERTS /app/san.conf && \ /app/derper \ --hostname=$DERP_HOST \ --certmode=manual \ --certdir=$DERP_CERTS \ --stun=$DERP_STUN \ --a=$DERP_ADDR \ --http-port=$DERP_HTTP_PORT \ --verify-clients=$DERP_VERIFY_CLIENTS
build_cert.sh
脚本创建自签名证书
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 27 28 29 30 31 32 #!/bin/bash CERT_HOST=$1 CERT_DIR=$2 CONF_FILE=$3 echo "[req] default_bits = 2048 distinguished_name = req_distinguished_name req_extensions = req_ext x509_extensions = v3_req prompt = no [req_distinguished_name] countryName = XX stateOrProvinceName = N/A localityName = N/A organizationName = Self-signed certificate commonName = $CERT_HOST : Self-signed certificate [req_ext] subjectAltName = @alt_names [v3_req] subjectAltName = @alt_names [alt_names] IP.1 = $CERT_HOST " > "$CONF_FILE " mkdir -p "$CERT_DIR " openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout "$CERT_DIR /$CERT_HOST .key" -out "$CERT_DIR /$CERT_HOST .crt" -config "$CONF_FILE "
构建镜像:
1 docker build -t zerchin/derper-ip:v0.1 .
总结 通过开源 Headscale 替代 Tailscale 控制平面,打破设备规模束缚并实现策略自主。自建 DERP 节点,将中继延迟从跨国绕行的几百 ms 压缩至 10ms 级。总而言之,自建 Headscale + DERP 真香!
参考链接:
https://warnerchen.github.io/2025/04/26/Tailscale-%E8%87%AA%E5%BB%BA%E4%B8%AD%E7%BB%A7/
https://icloudnative.io/posts/custom-derp-servers/ https://github.com/yangchuansheng/ip_derper/tree/main