Headscale自部署指南:用Docker搭建Tailscale开源替代方案

3372 字
17 分钟
Headscale自部署指南:用Docker搭建Tailscale开源替代方案

痛点:免费的Tailscale只有3个用户#

用 Tailscale 组网非常爽——装个客户端,登录账号,所有设备就在一个虚拟局域网里了。问题是:免费版最多 3 个用户

当你的设备超过 3 台——笔记本、台式机、两台服务器、手机——免费版直接不够用。付费版 $6/月起,一年下来也小几百块。而且数据经过 Tailscale 的中继服务器,虽然加密了,但总有那么点不放心。

Headscale 就是答案。

Headscale 是 Tailscale 控制服务器的开源实现,完全兼容 Tailscale 客户端。你只需要一台有公网 IP 的 VPS,就能自建一套功能完整的 Tailscale 组网服务。没有用户数限制,数据不经第三方,连 DERP 中继服务器也能自建。

本文用 Docker Compose 完成全套部署:Headscale 控制服务 + Caddy HTTPS 反向代理 + headscale-ui 管理面板 + 自建 DERP 中继。

Headscale ≠ VPN:理解 P2P Mesh 组网#

在动手部署之前,先搞清楚 Headscale 在整个组网中扮演的角色。

Tailscale/Headscale 不是传统的 VPN(Virtual Private Network),而是一种 P2P Mesh 网络。两者的核心区别:

特性传统 VPN(OpenVPN/WireGuard)P2P Mesh(Tailscale/Headscale)
拓扑中心辐射型(Hub-and-Spoke)全互联型(Full Mesh)
流量路径全部经过 VPN 服务器直连(P2P),不通则走中继
带宽瓶颈服务器带宽各节点带宽
单点故障服务器挂了网络全挂控制服务仅处理信令,挂了不影响已有连接
配置复杂度每台设备要写配置文件零配置,登录即用
适用场景公司远程接入内网个人/团队设备互联

Headscale 的工作机制用一个简单的流程就能说明:

设备A ──注册到──▶ Headscale 控制服务器
设备B ──注册到──▶ Headscale 控制服务器
控制服务器告诉 A:"B 的公网 IP 是 x.x.x.x:3478"
控制服务器告诉 B:"A 的公网 IP 是 y.y.y.y:3478"
设备A ◀───直接 UDP 连接───▶ 设备B
↕ NAT 穿透失败时走 DERP 中继

关键点:Headscale 只负责控制面——处理注册、分配 IP、交换节点信息。真正的数据传输是设备之间的直连 UDP(STUN 打洞),流量完全不经过 Headscale 服务器。只有 NAT 穿透失败时,才回退到 DERP 中继。

部署架构#

我们用 Docker Compose 部署三个服务:

┌─────────────────────────────────────┐
│ VPS(公网) │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Headscale │ │ Caddy │ │
│ │ :8080 │◀─│ HTTPS │────8080│
│ │ :9090 │ │ :443 │ │
│ └─────┬────┘ └──────────┘ │
│ │ │
│ ┌─────┴────┐ │
│ │ headscale │ │
│ │ -ui │ │
│ └──────────┘ │
│ │
│ 端口:443(HTTPS) 3478(STUN/DTLS) │
└─────────────────────────────────────┘
  • Headscale:核心控制服务,监听 8080(HTTP API)
  • Caddy:自动 HTTPS 反向代理,将域名流量转发给 Headscale
  • headscale-ui:Web 管理面板,方便管理节点和用户

环境要求#

  • 一台有公网 IP 的 VPS(1 核 1GB 内存足够)
  • 一个域名(例如 hs.example.com),解析到 VPS 的 IP
  • 安装 Docker 和 Docker Compose

Docker Compose 部署#

第一步:创建目录结构#

Terminal window
mkdir -p /opt/headscale/{config,data,ui} && cd /opt/headscale

第二步:编写 Docker Compose 配置#

新建 docker-compose.yml

version: "3.9"
services:
headscale:
image: headscale/headscale:0.29.1
container_name: headscale
restart: unless-stopped
volumes:
- ./config:/etc/headscale
- ./data:/var/lib/headscale
ports:
- "3478:3478/udp" # STUN/DERP over DTLS
command: serve
networks:
- headnet
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
container_name: headscale-ui
restart: unless-stopped
networks:
- headnet
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./data/caddy:/data
networks:
- headnet
networks:
headnet:
driver: bridge

注意:Headscale 官方并不正式支持 Docker 部署(文档建议直接二进制运行),但社区实践证明 Docker Compose 方案稳定可靠。

第三步:生成 Headscale 配置#

Terminal window
docker run --rm headscale/headscale:0.29.1 headscale config > config/config.yaml

编辑 config/config.yaml,修改关键参数:

# 你的域名
server_url: https://hs.example.com:443
# Headscale 内部监听地址
listen_addr: 0.0.0.0:8080
# 内部 gRPC(UI 使用)
metrics_listen_addr: 0.0.0.0:9090
# IP 分配范围
ip_prefixes:
- 100.64.0.0/10
# DERP 配置
derp:
server:
enabled: true
region_id: 999
region_code: "selfhost"
region_name: "Self-hosted DERP"
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_key
urls: []
auto_update_enabled: false
# 随机化节点端口(提高 STUN 打洞成功率)
randomize_client_port: true

第四步:编写 Caddyfile#

hs.example.com {
reverse_proxy headscale:8080
}
ui.hs.example.com {
reverse_proxy headscale-ui:80
}

第五步:启动#

Terminal window
docker compose up -d

检查状态:

Terminal window
docker compose ps
docker compose logs -f headscale

看到 Started headscale 日志就说明启动成功了。

创建用户并接入客户端#

创建用户#

Terminal window
# 在 headscale 容器中执行
docker compose exec headscale headscale users create myhome
# 创建预授权密钥(有效期24小时)
docker compose exec headscale headscale preauthkeys create --user myhome --expiration 24h

Linux/macOS 客户端接入#

在客户端设备上安装 Tailscale(注意:不是 Headscale 客户端,Tailscale 官方客户端完全兼容 Headscale):

Terminal window
# 安装 Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# 连接到自建 Headscale
sudo tailscale up --login-server=https://hs.example.com --authkey=<上一步生成的密钥>

浏览器打开返回的链接,登录后设备就加入网络了。

Windows 客户端#

  1. Tailscale 官网 下载 Windows 客户端
  2. 按住 Shift 右键点击系统托盘中的 Tailscale → Run tailscale up
  3. 在弹出的终端中输入:
Terminal window
tailscale up --login-server=https://hs.example.com --authkey=<密钥>

验证连接#

在任何一台加入网络的设备上:

Terminal window
tailscale status

你应该能看到所有已连接的设备:

100.64.0.1 headscale john@ linux -
100.64.0.2 laptop john@ macOS active; direct x.x.x.x:3478
100.64.0.3 server-prod john@ linux active; relay "selfhost"

direct 表示是直连(P2P 打洞成功),relay 表示走 DERP 中继。

DERP 中继部署与配置#

DERP(Detoured Encrypted Relay Protocol)是 Tailscale/Headscale 的保底机制——当两个设备之间的 NAT 穿透失败时(比如双方都在对称 NAT 后面),流量会通过 DERP 中继服务器转发。

自建 DERP 服务器#

如果上一步已经有设备显示 relay 状态,说明你已经在用自带的 DERP 了(因为配置中开启了 derp.server.enabled: true)。但实际上这个 DERP 和 Headscale 进程共用同一个端口和进程。更可靠的做法是单独跑一个 DERP 实例。

方案一:使用 Headscale 的 Dockerfile.derper 镜像

# 在 docker-compose.yml 中添加
services:
derper:
image: headscale/headscale:0.29.1
container_name: derper
restart: unless-stopped
entrypoint: ["derper"]
command: ["--hostname=hs.example.com", "--verify-clients=true"]
ports:
- "3478:3478/udp"
volumes:
- ./data/derper:/var/lib/headscale/derper
networks:
- headnet

方案二:独立 DERP 部署(更推荐)

不想增加复杂度的话,直接用 Headscale 内置的 DERP 即可,轻度使用完全够用。

检查 DERP 状态#

部署完 DERP 后,在 Headscale 容器中查看 DERP 节点列表确认是否正常工作:

Terminal window
docker compose exec headscale headscale debug derp

输出示例:

Regions:
999 (selfhost):
- node: selfhost-derp (STUN: 0.0.0.0:3478)
IPv4: hs.example.com
can dial: ✅ Home: 200ms

看到 can dial: ✅ 就表明 DERP 节点正常工作。如果显示 ,检查防火墙是否开放了 UDP 3478 端口,以及域名解析是否正确。

你也可以在客户端上验证 DERP 延迟:

Terminal window
# 在客户端上查看 DERP 延迟
tailscale status --json | jq '.Peer | to_entries[] | {name: .value.DNSName, relay: .value.Relay}'

理解 DERP 的选择逻辑#

Tailscale/Headscale 客户端在选择通信方式时遵循这个优先级:

  1. 直连(Direct):UDP 打洞成功 → 最佳性能,延迟最低
  2. 中继(Relay):NAT 穿透失败 → 走 DERP 中继,延迟稍高
  3. 不可达(Unreachable):中继也连不上 → 检查网络策略和防火墙

DERP 中继的带宽取决于 VPS 的带宽,所以如果大部分节点都是 relay 状态,建议升级 VPS 带宽或优化 NAT 穿透策略。

多 DERP 节点负载均衡#

如果你的设备分布在不同地区,可以部署多个 DERP 节点,客户端会自动选择延迟最低的那个:

# 在不同 VPS 上部署独立的 DERP 实例,然后在 Headscale 配置中注册
derp:
server:
enabled: true
region_id: 999
region_code: "selfhost"
region_name: "Self-hosted DERP"
stun_listen_addr: "0.0.0.0:3478"
# 手动添加其他 DERP 节点
urls: []
auto_update_enabled: false
nodes:
- region_id: 999
region_code: "selfhost"
region_name: "Self-hosted DERP"
# 可以添加多个节点
nodes:
- name: "derp-asia"
region_id: 999
hostname: "hs.example.com"
stun_port: 3478
stun_only: false
- name: "derp-eu"
region_id: 1000
hostname: "eu.hs.example.com"
stun_port: 3478
stun_only: false

公网 DERP fallback#

如果你想进一步保证连接成功率,可以加上 Tailscale 官方的公网 DERP 作为备选:

# config.yaml 中的 derp 配置
derp:
server:
enabled: true
region_id: 999
region_code: "selfhost"
region_name: "Self-hosted DERP"
stun_listen_addr: "0.0.0.0:3478"
# 用官方的 DERP 列表作为 fallback
urls:
- https://controlplane.tailscale.com/derpmap/default
auto_update_enabled: true

这样即使你的自建 DERP 挂了,流量会自动切换到公网 DERP。

使用 headscale-ui 管理#

浏览器打开 https://ui.hs.example.com,输入 Headscale API 地址:

API URL: https://hs.example.com

在 Headscale 中生成 API Key:

Terminal window
docker compose exec headscale headscale apikeys create

把生成的 Key 粘贴到 UI 中,就能可视化地管理所有节点了:

  • 查看在线/离线设备
  • 路由配置
  • 用户管理
  • 节点过期时间

运维技巧#

自动清理过期节点#

Terminal window
# 每小时清理过期节点
docker compose exec headscale headscale nodes expire --all

建议加到 crontab 中:

Terminal window
0 * * * * docker compose exec -T headscale headscale nodes expire --all

日志与监控#

日常运维中最常用的命令:

Terminal window
# 查看实时日志
docker compose logs -f headscale
# 查看已注册节点
docker compose exec headscale headscale nodes list
# 查看路由状态
docker compose exec headscale headscale routes list
# 查看用户列表
docker compose exec headscale headscale users list

如果想可视化监控节点在线状态,headscale-ui 管理面板比命令行直观得多——一眼就能看出哪些设备离线了、最近一次在线是什么时候。

升级 Headscale 版本#

Terminal window
cd /opt/headscale
# 拉取最新镜像
docker compose pull headscale
# 重新创建容器
docker compose up -d --force-recreate headscale
# 检查版本和迁移状态
docker compose logs headscale | grep -i "migration\|version"

Headscale 的数据迁移是自动执行的,升级前建议备份 data/ 目录:

Terminal window
tar -czf backup-$(date +%Y%m%d).tar.gz data/

启用 ACL 策略#

Headscale 支持基于标签的 ACL(访问控制列表),可以精确控制哪些设备能访问哪些设备:

config.yaml
acl_policy_path: /etc/headscale/acl.hujson

创建 config/acl.hujson

{
"groups": {
"group:admin": ["tag:admin"],
"group:dev": ["tag:dev"]
},
"acls": [
{"action": "accept", "src": ["tag:admin"], "dst": ["*:*"]},
{"action": "accept", "src": ["tag:dev"], "dst": ["tag:dev:*"]}
]
}

备份配置#

Terminal window
# 备份整个 headscale 数据目录
tar -czf headscale-backup-$(date +%Y%m%d).tar.gz /opt/headscale/data

Headscale vs Tailscale 对比#

特性Headscale(自建)Tailscale(官方)
费用仅付 VPS 费用(≈¥30/月)免费 3 用户,$6+/月起
用户数限制3(免费版)
数据隐私完全自控经官方中继
DERP 中继自建或公网 fallback全球节点
维护成本需自己维护升级零维护
官方客户端兼容✅ 完全兼容官方提供
稳定性取决于 VPSSLA 保障
管理面板headscale-ui(第三方)官方管理台

常见问题 FAQ#

Q:为什么我用 tailscale status 看到是 relay 而不是 direct?

A:说明 NAT 穿透没成功,流量走了 DERP 中继。首先确认设备的 UDP 端口(3478)能被双方访问。如果双方都在严格 NAT 后面(如公司网络 + 手机热点),基本只能走 relay,但延迟通常也在可接受范围内。

Q:Headscale 的 IP 段为什么要用 100.64.0.0/10?

A:这是 IANA 预留的运营商级 NAT(CGNAT)地址段,与内网地址(192.168.x.x/10.x.x.x)不冲突,也不会和公网 IP 冲突,是 Tailscale/Headscale 的默认分配段。

Q:迁移已有的 Tailscale 设备到 Headscale 需要重装吗?

A:不需要重装。直接在设备上执行 sudo tailscale logout 退出官方网络,然后 sudo tailscale up --login-server=https://hs.example.com 重新登录即可。

Q:Headscale 支持 MagicDNS 吗?

A:支持。在 config.yaml 中启用 dns.magic_dns: true,然后在客户端 tailscale up --accept-dns 即可通过设备名互相访问(例如 ping laptop 而不是 ping 100.64.0.2)。

Q:能否把已有的 WireGuard 配置导入 Headscale?

A:不能直接导入。Headscale 的节点管理完全通过控制平面进行,需要每台设备用 Tailscale 客户端重新注册。不过你可以在 Headscale 中手动添加子网路由,让 Headscale 网络访问现有 WireGuard 子网。

Q:Headscale 支持 exit node(出口节点)吗?

A:支持。在要作为 exit node 的设备上执行 sudo tailscale up --login-server=https://hs.example.com --advertise-routes=0.0.0.0/0,::/0,然后在 Headscale 中启用路由即可。这样所有设备可以经过该节点访问公网,适合用来做科学上网或远程办公网络出口。

Q:我用的是 frp/Cloudflare Tunnel 暴露内网服务,和 Headscale 比哪个好?

A:场景不同。frp 和 Cloudflare Tunnel 适合暴露单个端口给公网(如把内网的 Web 服务发布出去)。Headscale 适合设备互联——让你的所有设备在一个虚拟局域网里互相访问。如果只是发布一个 Web 服务,frp 更简单;如果你需要 SSH 到任意设备、传输文件、访问内网资源,Headscale 更合适。

Q:Headscale 控制服务器宕机了,现有连接会断吗?

A:不会。Headscale 控制服务器只负责注册和信令交换(handshake),设备之间的 P2P 连接一旦建立,就不依赖控制服务器。即使控制服务器宕机了,已有连接照常工作。只是新设备无法注册加入网络,已有连接的保活机制会继续运行。

总结#

Headscale 让你在 VPS 上自建一套完整的 P2P Mesh 组网服务,摆脱 Tailscale 免费版的 3 用户限制,同时数据完全由自己掌控。Docker Compose 部署方式让整个过程足够丝滑——从零到上线,半小时就能搞定。

有了 Headscale,你的所有设备——笔记本、服务器、树莓派、手机——都在一张网里。SSH 不用再开公网端口,文件传输用内网速度,数据库直连走加密通道。

部署清单回顾:

  1. ✅ 准备一台公网 VPS + 域名
  2. Docker Compose 部署 Headscale + Caddy + headscale-ui
  3. ✅ 创建用户和预授权密钥
  4. ✅ 客户端接入验证
  5. ✅ 可选:自建 DERP 中继 + ACL 策略

如果你在部署中遇到问题,欢迎评论区留言。

延伸阅读#

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Headscale自部署指南:用Docker搭建Tailscale开源替代方案
https://www.kshare.top/posts/headscale-guide/
作者
Kshare
发布于
2026-06-23
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Kshare
Hello, I'm Kshare.
公告
站点已开启广告(类型:弹窗广告),给您带来的不便敬请谅解。如有更好的广告建议,欢迎发送邮件至 t9uzrz6u@anonaddy.me,感谢您的支持!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
187
分类
9
标签
190
总字数
490,175
运行时长
0
最后活动
0 天前

文章目录