🛠️ 自建 Tailscale 控制服务器:Headscale + Headplane 部署完整指南
适用对象:具备 Docker 基础的运维人员或技术爱好者
核心组件:Headscale (v0.29+)、Headplane (Web UI)、Traefik (反向代理)
文档说明:本文档在保留原教程所有技术细节的基础上,优化了逻辑结构与排版,补充了关键注释,旨在作为一份专业的运维备忘录与教学手册。
隐私提示:文中所有 IP 地址、域名、密钥均已脱敏处理,请在实际操作中替换为您的实际参数。
📝 一、环境准备与规划
在开始部署前,请确保你的服务器已满足以下基础环境要求,并完成目录结构的规划。
1.1 基础环境依赖
- Docker:已安装并运行。
- Docker Compose:已安装(Plugin 形式或独立二进制)。
- 域名:拥有一个公网域名(文中统一使用
example.com 及其子域名作为示例)。
- 网络:服务器具备公网 IP 或处于可访问的内网环境中。
1.2 目录结构初始化
创建项目根目录及必要的数据存储路径。此结构便于后续的数据备份与迁移。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| mkdir -p ~/headscale-stack cd ~/headscale-stack
mkdir -p data/headscale/{config,data} mkdir -p data/headplane/{config,data}
|
⚙️ 二、核心组件配置
本章节包含两个核心组件的配置文件编写。请严格按照注释说明进行修改。
2.1 Headscale 服务配置 (config.yaml)
💡 原理注释:server_url 必须是 HTTPS 地址,因为 Tailscale 客户端强制要求安全连接。此处的端口 5001 是对外暴露的端口,而 listen_addr 是容器内部供 Traefik 代理的端口。
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 61 62 63 64 65
|
server_url: https://headscale.example.com:5001
listen_addr: 0.0.0.0:8080
grpc_listen_addr: 0.0.0.0:50443 grpc_allow_insecure: false
noise: private_key_path: /var/lib/headscale/noise_private.key
prefixes: v4: 100.64.0.0/10 v6: fd7a:115c:a1e0::/48 allocation: sequential
derp: server: enabled: true region_id: 999 region_code: "my-derp" region_name: "My Home DERP" verify_clients: false stun_listen_addr: "0.0.0.0:3478" private_key_path: /var/lib/headscale/derp_server_private.key urls: [] auto_update_enabled: false
database: type: sqlite sqlite: path: /var/lib/headscale/db.sqlite write_ahead_log: true
tls_cert_path: "" tls_key_path: ""
dns: magic_dns: true base_domain: headscale.internal override_local_dns: true nameservers: global: - 1.1.1.1 - 1.0.0.1
|
2.2 Headplane 管理界面配置
- 生成密钥:执行
openssl rand -hex 16 生成 32 位随机字符串。
- 配置文件:创建
./data/headplane/config/config.yaml。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
server: listen_addr: 0.0.0.0:3000 cookie_secret: "your_generated_32_byte_secret_here"
headscale: url: http://headscale:8080 data_dir: /var/lib/headplane
|
🐳 三、Docker Compose 部署与启动
3.1 编写 Compose 文件
⚠️ 注意事项:本配置依赖一个预先创建的外部 Docker 网络 ipbridge,以确保容器间通信。
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
|
version: '3.8'
services: headscale: image: headscale/headscale:latest container_name: headscale_svc restart: unless-stopped command: serve ports: - "8080:8080" - "50443:50443" - "3478:3478/udp" volumes: - ./data/headscale/config:/etc/headscale:ro - ./data/headscale/data:/var/lib/headscale networks: - ipbridge healthcheck: test: ["CMD", "headscale", "status"] interval: 30s timeout: 10s retries: 3
headplane: image: ghcr.io/tale/headplane:latest container_name: headplane_svc restart: unless-stopped ports: - "3000:3000" volumes: - ./data/headplane/config/config.yaml:/etc/headplane/config.yaml:ro - ./data/headplane/data:/var/lib/headplane networks: - ipbridge depends_on: - headscale
networks: ipbridge: external: true name: ipbridge
|
3.2 初始化与启动
在启动前,需确保数据目录权限正确,特别是 SQLite 数据库文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| docker network create ipbridge
cd ~/headscale-stack chmod -R 755 data touch data/headscale/data/db.sqlite
chmod 666 data/headscale/data/db.sqlite
docker compose pull docker compose up -d
docker compose logs -f
|
🌐 四、反向代理与域名接入 (Traefik)
Headscale 需要 HTTPS 访问,建议使用 Traefik 进行动态代理。
4.1 Traefik 动态配置
在 Traefik 的动态配置文件(如 dynamic.yaml)中添加以下路由规则。
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
| http: routers: headscale-api: rule: "Host(`headscale.example.com`)" entryPoints: ["websecure"] service: headscale-api-svc tls: certResolver: myresolver
headplane: rule: "Host(`headscale.example.com`) && PathPrefix(`/admin`)" entryPoints: ["websecure"] service: headplane-svc tls: certResolver: myresolver
derp: rule: "Host(`derp.example.com`)" entryPoints: ["websecure"] service: derp-svc tls: certResolver: myresolver
services: headplane-svc: loadBalancer: servers: - url: "http://<server_ip>:3000" derp-svc: loadBalancer: servers: - url: "http://<server_ip>:8080"
|
4.2 重载 Traefik
根据你的 Traefik 部署方式,执行重启或发送 SIGHUP 信号重载配置。
🔐 五、初始化配置与客户端接入
5.1 创建用户与密钥
进入容器执行初始化命令。注意:Headscale v0.29+ 使用用户 ID 而非用户名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| docker compose exec headscale headscale users create home
docker compose exec headscale headscale users list
docker compose exec headscale headscale preauthkeys create --user 1 --reusable --expiration 3650d
docker compose exec headscale headscale apikeys create --expiration 3650d
|
5.2 客户端连接命令
将 <auth-key> 替换为上一步生成的密钥。
| 平台 |
连接命令/方式 |
| Linux/macOS (CLI) |
tailscale up --login-server=https://headscale.example.com:5001 --auth-key=<密钥> --accept-routes=true |
| Windows (PS) |
tailscale up --login-server=https://headscale.example.com:5001 --auth-key=<密钥> --accept-routes=true |
| OpenWrt |
在 Web 界面或配置文件中设置 LoginServer 和 AuthKey |
| iOS/Android |
打开 App -> 设置 -> 使用自定义协调服务器 -> 输入 URL -> 登录 |
🛡️ 六、运维管理与故障排查
6.1 常用运维命令速查
1 2 3 4 5 6 7 8 9 10 11 12 13
| cd ~/headscale-stack
docker compose ps docker compose restart
docker compose exec headscale headscale nodes list docker compose exec headscale headscale nodes rename --identifier <ID> --new-name <新名称>
docker compose exec headscale headscale preauthkeys list --user 1
|
6.2 关键验证步骤
部署完成后,请按以下步骤验证服务是否正常:
- 健康检查:访问
https://headscale.example.com:5001/health,预期返回 {"status":"pass"}。
- Headplane 登录:访问
https://headscale.example.com:5001/admin,使用 API 密钥登录。
- DERP 状态:在客户端执行
tailscale netcheck,观察输出中是否包含 Region 999: derp.example.com:443 且状态为 ok。
6.3 常见问题 (FAQ)
- Q: 客户端连接提示证书错误?
- A: 如果是自签证书,客户端连接时需添加
--insecure 参数(仅限测试环境)。生产环境请确保域名证书有效。
- Q: DERP 连接失败?
- A: 检查防火墙是否放行 UDP 3478 端口,以及 Traefik 是否正确配置了
derp.example.com 的路由。
- Q: Headplane 提示版本不匹配?
- A:
Partial version match 警告通常不影响核心功能,只要 Headscale 和 Headplane 保持更新即可。