(运维)Tailscale 私有化部署方案——基于 Docker 编排与 Headscale 的全链路指南

前言:构建自主可控的“大内网”
在混合办公与家庭私有云盛行的今天,如何打通各个住所、公司与移动端的网络,成为了运维爱好者关注的焦点。相比于依赖公网端口映射的 FRP 方案,VPN(虚拟专用网络) 技术更侧重于网络层的互连,能够实现双向通信与 IP 级互连,安全性更高 。
在众多 VPN 协议中,WireGuard凭借其高性能与极简的内核级实现,成为了目前最先进的选择。然而,原生 WireGuard 配置繁琐,且缺乏中心化的管理面板 。
为此,我们采用了 Tailscale 协议体系,并通过私有化部署 **Tailscale的中继服务器(DERP)以及控制服务器(Headscale),构建一套完全自主、免费且高性能的异地组网方案 **。
(一):部署条件
1.拥有一台带有公网IP的云服务器(2mbps+带宽速率)(1)对于drep服务需要放通一个tcp和一个udp端口,我这里选择10700/tcp,10800/udp。(2)对于headscale服务(包括webui界面)需要放通两个tcp端口,我这里选择9090和9091。PS:drep服务器和headscale服务器可以放在同一个云服务器上。端口 协议 方向 用途
80, 443 TCP 入站 Web 服务、API 接入 (OpenResty、nginx服务)
9090 TCP 入站 WebUI 访问 (如果不配置域名反代)
9091 TCP 入站 仅限调试用
10800 UDP 入站/出站 DERP STUN 服务 (位于 DERP 节点服务器)
10700 TCP 入站 DERP HTTPS 通道 (位于 DERP 节点服务器) 2.持有一个域名(国内需备案)(1)阿里云等域名服务提供厂商都可以,通常10块钱上下一个。(2)国内云服务器使用域名需要备案,如果你是香港或国外的云服务器通常不需要备案。
3.安装docker和docker compose
这里推荐github上非常火的一键换源项目:Linuxmirror,这个项目可以帮我们一键安装和更新docker的镜像源(国内云服务器推荐使用国内镜像源或者会使用魔法也可以直接使用docker hub镜像源):
#安装docker和换源
bash <(curl -sSL https://linuxmirrors.cn/docker.sh)
#安装dockercompose组件
sudo apt install -y docker-compose-plugin (二):部署项目1.1 headscale的项目目录结构
/opt/headscale/
├── docker-compose.yaml # 容器编排文件
├── data/ # [自动生成] 数据库与密钥存储目录
└── config/ # 配置文件目录(需手动准备)
├── config.yaml # 主配置文件
└── derp.yaml # 自定义 DERP 节点映射表1.2 域名用途
核心服务 (API) hd.example.com 客户端连接、注册使用的核心地址 (走 HTTPS)
Web 管理面板 ui.example.com 图形化管理界面 (可选,或通过端口访问)
内网基础域名 net.example.com MagicDNS 分配给内网设备的后缀 (建议使用子域名以免冲突)
1.3 docker-compose.yaml配置/opt/headscale/docker-compose.yaml
version: '3.9'
services:
headscale:
image: headscale/headscale:latest
container_name: headscale
restart: unless-stopped
command: serve
volumes:
- ./config:/etc/headscale
- ./data:/var/lib/headscale
ports:
# 宿主机 9091 映射到容器 8080 (API端口)
- "9091:8080"
# 宿主机 9090 映射到容器 9090 (Metrics端口,可选)
- "9092:9090"
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
container_name: headscale-ui
restart: unless-stopped
environment:
- HTTP_PORT=80
ports:
# 宿主机 9090 映射到容器 80 (Web UI端口)
# OpenResty 反代目标端口(如果给UI单独域名):9090
- "9090:80"1.4 config.yaml的配置/opt/headscale/config/config.yaml
server_url: https://hd.example.com
# 监听所有IP的8080端口(容器内部端口)
listen_addr: 0.0.0.0:8080
# 监控地址
metrics_listen_addr: 0.0.0.0:9090
# gRPC 配置 (通常不需要改动)
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
# 密钥路径 (容器内路径,保持默认)
private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key
# IP段分配
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
allocation: sequential
# DERP 配置
derp:
server:
# 核心修改:关闭内置DERP,因为您有独立的DERP服务器
enabled: false
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
# 屏蔽官方DERP列表,使用纯净私有环境(可选)
urls: []
paths:
- /etc/headscale/derp.yaml
auto_update_enabled: false
update_frequency: 24h
# 禁用更新检查
disable_check_updates: true
ephemeral_node_inactivity_timeout: 30m
# 数据库配置
database:
type: sqlite
debug: false
sqlite:
path: /var/lib/headscale/db.sqlite
write_ahead_log: true
wal_autocheckpoint: 1000
# 外部NGINX处理 TLS,容器内部只走 HTTP
acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: ""
tls_letsencrypt_hostname: ""
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
tls_letsencrypt_challenge_type: HTTP-01
tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""
# 日志
log:
level: info
format: text
# 策略模式
policy:
mode: file
path: ""
# DNS 配置
dns:
magic_dns: true
base_domain: ts.net
# nameservers配置
nameservers:
global:
- 223.5.5.5
- 223.6.6.6
search_domains: []
extra_records: []
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
logtail:
enabled: false
randomize_client_port: false1.5 derp.yaml的配置/opt/headscale/config/derp.yamlPS:关于derp的配置下面会讲。
regions:
901:
regionid: 901
regioncode: derp1_code
regionname: derp1
nodes:
- name: bz1
regionid: 901
hostname: derp1.example.com
derpport: 10700
stunport: 10800
stunonly: false
902:
regionid: 902
regioncode: drep2_code
regionname: drep2
nodes:
- name: ali1
regionid: 902
hostname: derp2.example.com
derpport: 10700
stunport: 10800
stunonly: false1.6 反向代理配置
因为我的方案将TLS等加密认证都分配给了外部的nginx(用的是1panel面板的openresty)。
使用openresty配置反向代理:

修改hd.example.com域名的反向代理配置文件:hd.example.com域名一定要修改成自己的域名
server {
listen 80;
listen 443 ssl;
server_name hd.example.com;
index index.php index.html index.htm default.php default.htm default.html;
access_log /www/sites/hd.example.com/log/access.log main;
error_log /www/sites/hd.example.com/log/error.log;
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) {
return 404;
}
location ^~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
return 403;
}
root /www/sites/hd.example.com/index;
http2 on;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
ssl_certificate /www/sites/hd.example.com/ssl/fullchain.pem;
ssl_certificate_key /www/sites/hd.example.com/ssl/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header X-Forwarded-Proto https;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
location / {
proxy_pass http://127.0.0.1:9091;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
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 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
}如果使用Nginx或者NPM做反向代理的可以将我的配置文件甩给AI询问方法即可。
1.7 运行容器编排在/opt/headscale这个路径下运行:
cd /opt/headscale
docker compose up -d在查看docker是否运行成功
docker ps -n2
在使用docker logs验证headscale容器内是否正常
docker logs headscale
看到监听端口信息后可以确定容器正常运行1.8 登录配置headscale(1)浏览器地址输入hdui.example.com(你自己的网址)

看到这里就说明headscale部署成功了(2)去服务器中输入命令
docker exec headscale headscale apikeys create --expiration 9999d得到一个密钥信息(一定要保存好)

(3)点击图片右侧settings按钮保存headscale服务器信息

之后进入到User view界面,点击+New User

名称自定义,例如admin,创建好后点击对勾

这样用户就创建好了。到目前为止,Headscale服务器+Headscale的WebUI我们就搭建完成了,接下来搭建DERP服务器。2.1 DERP服务的项目目录结构
/opt/derp/
├── docker-compose.yaml # 下方的编排文件
└── certs/ # 证书存放目录
├── fullchain.pem # 域名证书公钥
└── privkey.pem # 域名证书私钥如果没有申请TLS证书,可以忽略掉存放证书的certs,建议还是使用1panel申请TLS证书用于HTTPS加密。我这里以使用HTTPS加密的配置为例。2.2 上传证书并配置docker-compose.yaml申请好的证书下载证书文件,解压后可以获得两个文件
fullchain.pem
privkey.pem将这个两个文件一同上传到certs目录下。编辑/opt/derp下的docker-compose.yaml文件
version: '3.8'
services:
derper:
image: hausen1012/ip_derper
container_name: derper
restart: always
ports:
- "10700:10700" # DERP 端口 (TCP)
- "10800:10800/udp" # STUN 端口 (UDP)
volumes:
# 将当前目录下的证书映射为容器内识别的格式 (文件名需对应域名)
- ./certs/fullchain.pem:/app/certs/derp.example.com.crt:ro
- ./certs/privkey.pem:/app/certs/derp.example.com.key:ro
command: >
/app/derper
-hostname example.com
-a :10700
-stun-port 10800
-http-port -1
-certmode manual
-certdir /app/certs2.3 运行容器编排在/opt/headscale这个路径下运行:
cd /opt/headscale
docker compose up -d在查看docker是否运行成功
docker ps -n1登录https://drep.example.com:10700 :

如果没有配置证书的话输入http://derp.example.com:10700

看到这个两个页面都代表DERP服务器已经正常启动成功了不要忘记将/headscale/config/derp.yaml中的域名替换为你自己的域名(三):配置Tailscale客户端1.下载tailscale客户端下载网址https://tailscale.com/download注意:这个下载网址有时候需要科学上网。

2.配置tailscale客户端,我以Windows客户端为例(其他系统客户端配置流程都一样)

这样表示tailscale安装成功了(1) win+R 输入CMD进入命令提示符输入
tailscale up --login-server=https://hd.example.uno
(2)看到出现这串网址后,复制网址到浏览器打开后:(3)再次回到hdui.example.com

回到配置tailscale的客户端后发现

(4)检测tailscale是否正常工作
tailscale staus #检测你的tailscale状态
tailscale netcheck #检测tailscale的网络情况 
这样整套的服务DERP中继+Tailscale客户端+headscale服务端就完成了,至于可不可以走p2p连接主要取决于你客户端所在网络的NAT类型和支不支持UDP打洞,但是中继服务器已经搭建完成。
最后将整个组网的流程图放到下方(由于不专业可能有错误或表达不清晰,欢迎指正修改)


