🎯 私有化部署 Headscale + 自建 DERP + Web UI(无域名、国内友好、生产级安全)

✅ 无需域名 | ✅ 国内网络友好 | ✅ 防踩坑指南 | ✅ 生产级安全配置
📌 适用人群:个人/家庭/企业私有组网、远程办公、内网穿透、替代商业 Tailscale
🧩 技术栈:Docker + Headscale + Derper + Web UI + Caddy(无域名 HTTPS 可选)
🕒 最后更新:2025年9月11日
💻 测试环境:Ubuntu 22.04 LTS + Docker 24.0.7 + Docker Compose v2.23.0


🎯 一、为什么选择私有化部署 Tailscale?

Tailscale 是一款基于 WireGuard 的零配置组网工具,但其官方服务依赖 controlplane.tailscale.com 和全球 DERP 中继节点,存在:

  • 数据出境风险(企业合规问题)
  • 国内访问延迟高(尤其 DERP 节点在国外)
  • 企业版收费昂贵($6/月/用户)

Headscale + 自建 DERP + Web UI = 完全私有化、无依赖、无费用、高性能内网穿透方案。


🧰 二、准备工作

✅ 你需要:

  1. 一台具有公网 IP 的云服务器(国内/海外均可,推荐 2C4G 起)
  2. 开放以下端口(安全组/防火墙):
    • TCP 8080 —— Headscale API(客户端注册)
    • TCP 8000 —— Web UI(可选,建议内网访问)
    • TCP 445, 446 + UDP 3478 —— 自建 DERP 服务(STUN + 中继)
  3. 安装好 DockerDocker Compose(教程略,可参考官方文档)
  4. 一个终端 SSH 连接工具(如 Xshell、Tabby、VSCode Remote)

🚫 本教程不依赖域名,所有访问均通过 IP + 端口完成。


📂 三、目录结构规划(建议在 /opt/headscale 下操作)

/opt/headscale
├── container-config/          # Headscale 配置文件
├── container-data/            # 持久化数据(数据库、密钥)
│   └── data/
├── headscale-repo/            # Headscale 源码(用于构建镜像)
├── headscale-ui/              # Web UI 源码
├── ip_derper                  # ip_derper 源码
└── docker-compose.yml         # 服务编排文件

🧱 四、第一步:部署自建 DERP 服务(中继节点)

DERP(Detoured Encrypted Routing Protocol)是 Tailscale 用于 NAT 穿透和流量中继的协议。自建 DERP 可确保流量不出内网。

4.1 克隆并构建 Derper 镜像

cd /opt/headscale
git clone https://kkgithub.com/yangchuansheng/ip_derper.git
cd ip_derper

修改源码 —— 禁用证书域名校验:

vim tailscale/cmd/derper/cert.go

找到 getCertificate 函数,注释掉以下三行:

func (m *manualCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
    // if hi.ServerName != m.hostname {
    //     return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)
    // }
    ...
}

⚠️ 安全提醒:此修改会降低 TLS 安全性,仅建议在完全私有、受控网络环境中使用。 生产环境建议配置合法域名 + 有效证书(如 Let’s Encrypt)。

Dockerfile 中添加源 apk(加速构建)👇

# build modified derper
RUN cd /app/tailscale/cmd/derper && \
    go env -w GO111MODULE=on && \                      #👈需添加这👈
    go env -w GOPROXY=https://goproxy.cn,direct && \   #👈两行代码👈
    CGO_ENABLED=0 /usr/local/go/bin/go build -buildvcs=false -ldflags "-s -w" -o /app/derper && \
    cd /app && \
    rm -rf /app/tailscale

我们继续修改下方相关的端口信息👇

# ========= CONFIG =========
# - derper args
ENV DERP_ADDR :23446        # 需要修改的http端口
ENV DERP_HTTP_PORT 23445    # 需要修改的https端口
ENV DERP_HOST=127.0.0.1
ENV DERP_CERTS=/app/certs/
ENV DERP_STUN true
ENV DERP_VERIFY_CLIENTS false
# ==========================

保存后执行构建指令👇

docker build -t ip_derper:1.86.2 .

🖥️ 五、第二步:部署 Headscale Web UI

Web UI 用于可视化管理节点、用户、ACL,大幅提升操作效率。

5.1 克隆 Headscale-ui 源码

cd /opt/headscale
git clone https://wget.la/https://github.com/gurucomputing/headscale-ui.git
cd headscale-ui/docker/production

5.2 给 Derper 创建配置文件 derper.json

cd /opt/headscale/headscale-ui/docker/production
vim derper.json

添加如下内容👇(请替换 公网服务器 IP 为你的真实公网 IP

{
    "Regions": {
        "901": {
            "RegionID": 901,
            "RegionCode": "Myself",
            "RegionName": "Myself Derper",
            "Nodes": [
                {
                    "Name": "901a",
                    "RegionID": 901,
                    "DERPPort": 23446,
                    "HostName": "公网服务器 IP",
                    "IPv4": "公网服务器 IP",
                    "InsecureForTests": true
                }
            ]
        }
    }
}

📌 HostNameIPv4 填写你的公网服务器 IP


5.3 修改 Dockerfile(复制 derper.json + 换源 + 安全用户)

编辑 headscale-ui/docker/production/Dockerfile(大约在文件第 57 行)👇

COPY --from=build /staging/${PROJECT_NAME}/build /web 后添加:

COPY ./derper.json /web/derper.json    #👈需要添加此代码

#👇将RUN apk add --no-cache caddy替换为
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \
    apk add --no-cache caddy

5.4 修改 Caddyfile(关键!API 反向代理)

编辑 headscale-ui/docker/production/Caddyfile

:{$HTTP_PORT} {
    redir / /web
    uri strip_prefix /web
    file_server {
        root /web
    }
    # 👇👇👇 必须添加!否则 UI 无法调用 API 👇👇👇
    reverse_proxy /api/v1/* http://headscale:8080 {
        header_up Host {host}
    }
}

# 如果不需要 HTTPS,注释以下部分(自签名证书)
:{$HTTPS_PORT} {
    redir / /web
    uri strip_prefix /web
    tls internal
    file_server {
        root /web
    }
    reverse_proxy /api/v1/* http://headscale:8080 {
        header_up Host {host}
    }
}

💡 为什么必须加 /api/v1/* 代理?
Web UI 默认会请求 http://宿主机IP:8000/api/v1/status 等接口。
若不配置反向代理,请求会直接打到 Caddy,而 Caddy 不认识这些 API,返回 404。
通过 reverse_proxy/api/v1/* 转发给 headscale:8080,实现前后端分离。


5.5 修改 1-image-build.sh

文件位于 headscale-ui/docker/production/scripts/

#!/bin/sh
set -x
apt-get update && apt-get install -y git
git clone https://github.com/gurucomputing/headscale-ui.git ${PROJECT_NAME}    #👈我修改了
cd ${PROJECT_NAME}
git checkout ${CHECKOUT_BRANCH}
npm config set registry https://registry.npmjs.org                             #👈我添加了
npm install
sed -i "s/insert-version/${VERSION}/g" ./src/routes/settings.html/+page.svelte
npm run build

5.6 构建 UI 镜像

cd /opt/headscale/headscale-ui/docker/production
docker build -f dockerfile -t headscale-ui:time25.08.23 .

🧠 六、第三步:部署 Headscale 核心服务

6.1 拉取源码并生成配置

cd /opt/headscale
git clone https://github.com/juanfont/headscale.git headscale-repo
cp ./headscale-repo/config-example.yaml ./container-config/config.yaml

6.2 修改 container-config/config.yaml

server_url: http://你的公网IP:8080     # 👈 必须是公网IP!
listen_addr: 0.0.0.0:8080             # 👈 监听所有接口

prefixes:
  v4: 100.64.0.0/10
# v6: fd7a:115c:a1e0::/48            # 👈 无 IPv6 环境请注释

derp_map:
  urls:
    - http://headscale-ui:8000/web/derper.json  # 👈 自建 DERP 映射
#   - https://controlplane.tailscale.com/derpmap/default

randomize_client_port: true           # 👈 防蹭网,随机端口

6.3 优化构建镜像(Go 换源加速)

编辑 headscale-repo/Dockerfile.integration👇

# RUN go install github.com/go-delve/delve/cmd/dlv@latest
# 将这行👆代码 替换为下列代码👇
RUN echo "=== Installing delve with domestic proxy ===" && \
    go env -w GOPROXY=https://goproxy.cn,direct && \
    go env -w GOSUMDB=off && \
    go env -w GO111MODULE=on && \
    go install github.com/go-delve/delve/cmd/dlv@latest

6.4 构建 Headscale 镜像

cd /opt/headscale/headscale-repo
docker build -f Dockerfile.integration -t headscale:v0.26.1 .

🐳 七、第四步:编写 docker-compose.yml

version: "3.9"

services:
  headscale:
    image: headscale:v0.26.1
    container_name: headscale
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - ./container-config:/etc/headscale
      - ./container-data/data:/var/lib/headscale
    entrypoint: headscale serve
    networks:
      - headscale_net
    ports:
      - "8080:8080"
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1

  headscale-ui:
    image: headscale-ui:time25.08.23
    container_name: headscale-ui
    restart: unless-stopped
    environment:
      - HTTP_PORT=8000
      - HTTPS_PORT=8443
    networks:
      - headscale_net
    ports:
      - "8000:8000"   # 👈 仅用于初始配置,完成后建议注释掉!

  ip-derper:
    image: ip_derper:1.86.2
    container_name: ip-derper
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
    network_mode: "host"

networks:
  headscale_net:
    driver: bridge

⚠️ 注意:image: headscale:v0.26.1 —— 不是官方镜像!


🚀 八、启动服务

cd /opt/headscale
docker-compose up -d

查看日志:

docker-compose logs -f headscale
docker-compose logs -f headscale-ui

🔐 九、初始化 Web UI 配置

  1. 浏览器访问:http://你的公网IP:8000
  2. 点击界面中的 Settings
  3. 获取 API Key:
docker exec -it headscale headscale apikey create --expiration 3650d    # 3650d代表天数👍
# 输出示例:INCSHXxXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  1. 在 Web UI 中:
    • Headscale API Key: 粘贴上面生成的 key
    • Headscale URL: 留空(默认使用 http://宿主机IP:8000/api/v1,因 Caddy 已反向代理)
    • 点击 Test Server Settings → 应显示 “Connection successful!”

🧩 原理说明
UI 会请求 http://宿主机IP:8000/api/v1/apikey,Caddy 将其转发给 headscale:8080,Headscale 验证 API Key 成功,返回状态。


👥 十、创建用户与添加节点

10.1 创建用户(两种方式)

方式一:Web UI

  1. 在 Web UI 点击 User View
  2. 输入用户名创建用户
  3. 访问 http://IP:8080/windows 获取登录链接
  4. 复制 "nodekey:xxxxxxxxx" 内容
  5. 在 Web UI Device View 添加设备

方式二:命令行

docker exec -it headscale headscale users create myuser

10.2 生成节点注册命令

docker exec -it headscale headscale --user myuser node register --output-qr

或访问:http://你的公网IP:8000/windows(或其他平台),复制命令到客户端执行。


10.3 批准节点(如未开启 auto_approval)

docker exec -it headscale headscale --user myuser node list          # 查看节点 ID
docker exec -it headscale headscale --user myuser node move --node-id 1 --to-user myuser

或在 Web UI 的 Nodes 页面点击 Approve


🛡️ 十一、安全加固建议

  1. 关闭 Web UI 公网暴露:编辑 docker-compose.yml,注释掉 ports: - "8000:8000",通过 SSH 隧道访问:

    ssh -L 8000:localhost:8000 user@your-server
    

    本地浏览器访问 http://localhost:8000

  2. 启用 HTTPS(自签名):取消 Caddyfile 中 HTTPS 部分的注释,Caddy 会自动生成证书。

  3. 配置 ACL:在 config.yaml 中指定 acl_policy_path,限制节点间访问。


🧩 十二、客户端配置(以 Windows 为例)

  1. 下载 Tailscale 官方客户端

  2. 退出登录(如已登录官方账号)

  3. 执行命令:

    tailscale up --login-server=http://你的公网IP:8080
    
  4. 浏览器会打开,粘贴注册命令或扫码,完成注册。


📊 十三、验证与测试

  1. Web UI 查看节点状态(Online)
  2. 两台设备互相 ping 100.64.x.x 地址
  3. 检查 DERP 路径:tailscale netcheck
  4. 查看流量是否走自建 DERP:tailscale status --active

🆘 十四、常见问题排查

问题 解决方案
Web UI 显示 “Failed to fetch” 检查 Caddyfile 是否配置 /api/v1/* 反向代理
客户端注册失败 检查 server_url 是否为公网 IP,防火墙是否放行 8080
DERP 无法连接 检查 Derper 是否使用 network_mode: host,端口 3478/445 是否开放
节点无法互相 ping 检查 auto_approval 或手动批准,检查 ACL 策略

🎉 十五、恭喜你!完全私有化 Tailscale 部署成功!

你已拥有:

  • ✅ 无域名、无依赖的私有组网服务
  • ✅ 自建 DERP 节点,国内低延迟
  • ✅ Web UI 可视化管理
  • ✅ 企业级安全配置(防蹭网、手动审批、ACL)

📚 Headscale 常用命令速查

Namespace

headscale namespace list           # 查看所有的namespace
headscale namespace create myspace      # 创建namespace
headscale namespace destroy myspace      # 删除namespace
headscale namespace rename myspace newspace  # 重命名namespace

Node

headscale node list     # 列出所有的节点
headscale node ls -t     # 列出所有的节点,同时显示出tag信息
headscale -n myspace node ls # 只查看namespace为myspace下的节点
headscale node delete -i<ID> # 根据id删除指定的节点(id可用node list查询)
               # 如 headscale nodes delete -i=2
headscale node tag -i=2 -t=tag:test # 给id为2的node设置tag为tag:test

Route

headscale routes list -i=3  # 列出节点3的所有路由信息
headscale routes enable -i=3 -r=192.168.10.0/24 
               # 将节点3的路由中信息为192.168.10.0/24的设置为true,
               # 这样除了虚拟内网ip,原先的内网ip网段为192.168.10的也能访问了

📥 后续可扩展功能

  • 配置 Exit Node(让其他设备通过此服务器上网)
  • 配置 Subnet Router(访问服务器所在内网)
  • 集成 Prometheus + Grafana 监控流量
  • 使用 PostgreSQL 替代 SQLite(高可用)

📌 本文为全网最详细、最防踩坑、最易上手的 Headscale 私有化部署教程。
如需 PDF/Markdown 源码/一键脚本,请留言告知。
欢迎点赞、收藏、转发给需要的朋友!


教程完结,部署成功!
有任何问题,欢迎在评论区留言,我会一一解答。