云计算百科
云计算领域专业知识百科平台

Headscale 容器化部署完整指南:一条 docker run 跑起私有 Tailscale 控制服务器

Headscale 容器化部署完整指南:一条 docker run 跑起私有 Tailscale 控制服务器

Headscale 是一个开源、可自托管的 Tailscale 控制服务器实现。装好它之后,所有 Tailscale 客户端都指向你自己部署的这台服务,设备注册、命名网络、DERP 中继和 ACL 策略全部由你掌控,不再依赖官方控制平面。

整篇文章按四步展开:先看它到底能替你做什么;再按当前版本把容器从零跑通,包括建目录、改配置、启动、建用户、发密钥、注册节点;然后是日常会用到的核心命令速查表;最后收录几个高频翻车场景的修复方法,外加一份紧凑的升级操作清单。

它到底能干什么

Headscale 私有 Tailscale 控制服务器部署架构示意

  • 替代官方 controlplane:客户端 tailscale up –login-server 指向你的域名,注册、登录、节点管理全在自己手里。
  • 用 users 做身份隔离,用 policy(ACL)控制哪些节点之间能互通、谁能走出口路由。
  • 用 preauthkeys 给无浏览器环境的机器发预授权密钥,一条命令完成准入。
  • 自带 DERP 中继配置与健康检查端点(/health),跨 NAT 不通时有兜底。

和直接用 Tailscale 官方服务相比,差异点就在于控制面可审计、策略可版本化、数据不出自己的机器;代价是要自己维护这台服务器和它的前置 TLS 反代。

当前版本完整部署实操

容器跑起来之前,先准备好两个挂载目录:一个放配置,一个放 SQLite 数据库。目录结构一旦定下,升级时直接复用即可。

# 目录准备:config 放配置,lib 放数据库
mkdir -p ./headscale/{config,lib}
cd ./headscale

# 从仓库拉一份配置模板作为起点(版本号按实际发布替换)
curl -o config/config.yaml https://gitcode.com/GitHub_Trending/he/headscale/raw/main/derp-example.yaml

配置文件里有几个参数必须改对,改错会导致容器启动即退出:

参数说明注意
server_url 客户端访问控制面的地址,必须带 https 且有有效证书 不能与 base_domain 同域且无法区分
dns.base_domain MagicDNS 的私有域名 用你自己的域名,如 ts.example.net
derp.auto_update / DERP 区域 中继配置 官方 DERP 区域建议自行部署替代,模板里 1: null 即禁用内置区域

注意:server_url 的域名必须能被客户端真实解析到(前置 nginx/caddy 反代到 8080 端口),否则客户端注册时会卡在连接超时。配置项全集见 docs/ref/configuration.md。

目录和配置就位后,启动容器。下面这条命令一次把只读文件系统、tmpfs、卷挂载、端口和健康检查都配齐:

docker run \\
–name headscale \\
–detach \\
–read-only \\
–tmpfs /tmp \\
–tmpfs /var/run/headscale \\
–volume "$(pwd)/config:/etc/headscale:ro" \\
–volume "$(pwd)/lib:/var/lib/headscale" \\
–publish 127.0.0.1:8080:8080 \\
–publish 127.0.0.1:9090:9090 \\
–health-cmd "CMD headscale health" \\
headscale/headscale:latest \\
serve

serve 是容器的启动子命令,镜像入口默认就是它,命令末尾不需要再带 headscale 前缀。端口先绑 127.0.0.1,等反代跑通后再改成 0.0.0.0 对外暴露。

容器跑起来之后,第一件事是确认它活着:

docker logs –follow headscale
curl http://127.0.0.1:8080/health

健康端点返回正常后,往网络里加第一个成员:建用户,再给这个用户发一把可复用的预授权密钥(默认 1 小时过期,–expiration 可调):

# 建用户
docker exec -it headscale headscale users create alice

# 发密钥:可复用、24 小时有效
docker exec -it headscale headscale preauthkeys create –user alice –reusable –expiration 24h

有了密钥,客户端侧就能自助注册。Linux 机器上执行 tailscale up 并指向你的服务地址,把密钥作为 –auth-key 传入:

tailscale up –login-server https://headscale.example.com \\
–auth-key <上一步输出的密钥>

如果走的是 Web 认证流程(浏览器登录),客户端会返回一个 Auth ID,在服务器上批准即可,这条命令是当前的标准写法:

docker exec -it headscale headscale auth register –user alice –auth-id <AUTH_ID>

注:旧版 headscale nodes register –user <user> –key <KEY> 写法已在源码中标记废弃,等价能力由 headscale auth register 承担,老脚本需要替换。命令定义可见 cmd/headscale/cli/nodes.go 和 cmd/headscale/cli/auth.go。

注册完成后回服务器侧验收:

docker exec -it headscale headscale nodes list -u alice

节点出现且在线,整条链路就通了。需要出口路由时再批准路由:

# 查看某节点宣告的路由
docker exec -it headscale headscale nodes list-routes –identifier 1

# 批准 0.0.0.0/0 出口路由
docker exec -it headscale headscale nodes approve-routes –identifier 1 –routes 0.0.0.0/0,::/0

核心命令速查

按使用频率排的常用命令都在容器里执行(原生安装则去掉 docker exec -it headscale 前缀):

命令作用关键参数
headscale users create 创建用户 <USER> 用户名
headscale preauthkeys create 发预授权密钥 –user –reusable –expiration
headscale nodes list 查看节点与状态 -u 按用户过滤
headscale auth register 批准节点注册 –user –auth-id
headscale nodes approve-routes 批准出口路由 –identifier –routes
headscale policy get / set 查看/下发 ACL 策略 set 需 -f 指定 HuJSON 文件

踩坑点:

策略文件是 HuJSON 格式(JSON 里可以写注释),policy set 之前建议先 policy check 校验一遍,语法错误会被整体拒绝。

nodes list-routes、approve-routes 的定位参数统一叫 –identifier(也可写 -i),旧版路由命令的 -n 节点参数不再沿用。

预授权密钥默认 1 小时过期且一次性;批量装机时记得加 –reusable 并拉长 –expiration,否则第二台机器注册时密钥已经失效。命令实现见 cmd/headscale/cli/preauthkeys.go。

翻车现场与修复

容器起来就退,日志报找不到 config.yaml

现象:docker run 后容器立刻退出,日志里出现 config.yaml: no such file or directory。

根因:挂载点存在但 config.yaml 没放进去,或模板下错文件(比如把 derp 示例当主配置,缺了 server_url 等必填项)。

修复:

ls -la ./headscale/config/ # 确认 config.yaml 存在且内容完整
docker logs headscale # 重跑前先看完整报错
docker logs –follow headscale # 修完配置后重新跟踪日志

users create 报 user already exists,但列表里没有这个人

现象:headscale users create alice 提示重名,users list 却查不到。

根因:早前版本调整过用户 ID 管理逻辑,数据库里可能残留一条无关联记录的 user 行;也可能是同名但大小写不同。

修复(镜像默认基于 distroless,没有 shell,换 -debug 标签的镜像才有 Busybox 可以进):

# debug 镜像与正式版数据兼容,可交替使用
docker run -it –rm -v ./headscale/lib:/var/lib/headscale \\
headscale/headscale:latest-debug sh

# 进入后定位残留记录并清理
sqlite3 /var/lib/headscale/db.sqlite "SELECT id, name FROM users;"
sqlite3 /var/lib/headscale/db.sqlite "DELETE FROM users WHERE name='alice';"

表结构可对照 hscontrol/db/schema.sql 确认字段。

节点显示在线,但互相 ping 不通

现象:nodes list 里两台设备都 online,跨节点流量却不通。

根因:ACL 策略没放行——默认策略下节点之间并不互通;或者两端 NAT 都不利而 DERP 中继被禁用。

修复:

# 看当前策略
docker exec -it headscale headscale policy get

# 下发一条允许 tag 互访的 HuJSON 策略
docker exec -it headscale headscale policy set -f ./policy.hujson

策略写法与内置动作见 docs/ref/policy.md。

nodes register 报 deprecated

现象:沿用老脚本执行 headscale nodes register,命令能跑但输出带废弃警告。

根因:该命令在当前版本已标记 Deprecated,注册入口统一到 auth 子命令下。

修复:

docker exec -it headscale headscale auth register –user <USER> –auth-id <AUTH_ID>

升级操作清单

  • 先备份数据库:cp ./headscale/lib/db.sqlite ./backup/db.sqlite.$(date +%F)。
  • 拉取目标版本镜像:docker pull headscale/headscale:<新版本>。
  • 对照新版配置模板补齐新增参数(server_url、dns.base_domain 校验在新版更严格,server_url 与 base_domain 不能互相包含导致不可达)。
  • 换镜像重启容器,启动时自动执行数据库迁移,无需手工操作;迁移是否成功看 docker logs headscale。
  • 验证:curl http://127.0.0.1:8080/health 和 headscale nodes list 都正常即完成。
  • 注意:较新的发布只支持从紧邻的上一大版本升级(例如 0.30 只接受 0.29 的库),跨多个版本要逐级升;且凭证表结构在 0.30 有不可回退的合并,备份是唯一回退手段,详见 CHANGELOG.md。

    headscale 的容器化部署本质就是"一份配置 + 一个数据库目录 + 一条 serve 命令",命令面不大,跑通一次后日常就是上面速查表里那几条。

    延伸阅读:docs/about/faq.md

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Headscale 容器化部署完整指南:一条 docker run 跑起私有 Tailscale 控制服务器
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!