





















Tailscale 是一款基于 WireGuard 的现代虚拟专用网方案。尽管其客户端开源且免费计划的 3 用户与 100 设备限制已经足够慷慨,但对于追求完全自主控制权、希望摆脱账户限制的用户来说,自托管控制服务器是更优的选择。
Headscale 是 Tailscale 控制服务器的开源自托管实现,其角色定位类似于 Bitwarden 生态中的 Vaultwarden。本质上,Headscale 仅作为虚拟网络的 控制平面(Control Plane),只负责节点注册、路由分发、子网声明以及出口节点配置,并不直接参与流量传输。
对于无法实现点对点直连的复杂网络环境,它也支持启用 DERP 服务器并进行流量中继(此部分配置不在本文讨论范围内)。

Headscale 提供了多种部署方式,在 Alpine Linux 3.23 环境下,推荐直接使用包管理器安装:
sudo apk update
sudo apk add headscale headscale-openrc
如果是 Debian 13 GNU/Linux:
HEADSCALE_VERSION="0.28.0" # 设置版本号
HEADSCALE_ARCH="amd64" # 系统架构,比如 amd64
wget --output-document=headscale.deb \
"https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
sudo apt install ./headscale.deb
rm headscale.deb
编辑 /etc/headscale/config.yaml,调整以下核心参数:
# 访问 Headscale 的公网域名
server_url: https://headscale.your.domain
# 服务监听地址
listen_addr: 127.0.0.1:8080
# 禁用 gRPC 监听(默认使用 Unix Socket 即可)
grpc_listen_addr: ""
# 配置 Magic DNS 域名
dns:
base_domain: dejavu.local
配置完成后,设置开机自启并启动服务:
# Alpine Linux
sudo rc-update add headscale default
sudo rc-service headscale start
sudo rc-service headscale status
# Debian Linux
sudo systemctl enable --now headscale
sudo systemctl status headscale
以 Nginx 为例,下面是一个典型的虚拟主机示例配置:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name headscale.your.domain;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name headscale.your.domain;
ssl_certificate /path/to/cert/headscale.your.domain.pem;
ssl_certificate_key /path/to/cert/headscale.your.domain.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519:P-256:P-384;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE+AES128:RSA+AES128:ECDHE+AES256:RSA+AES256';
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
access_log /var/log/nginx/headscale.your.domain.access.log;
error_log /var/log/nginx/headscale.your.domain.error.log;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'none'; script-src 'none'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self';" always;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 1200s;
proxy_send_timeout 1200s;
}
}
重载 Nginx 配置以应用更改:
sudo nginx -t && sudo nginx -s reload
Alpine Linux:
sudo apk update && sudo apk add tailscale
sudo rc-update add tailscale default
sudo rc-service tailscale start
Debian 13:
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkgs.tailscale.com/stable/debian/trixie.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/debian/trixie.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
sudo apt update && sudo apt install tailscale -y
针对 Tailscale 点对点连接所需的 Nftables 配置,将它们附加到现有配置里:
#!/usr/sbin/nft -f
# 清空所有规则
flush ruleset
table inet filter {
# ============================================================
# INPUT 链 (入站)
# ============================================================
chain input {
# ...
iifname "tailscale0" accept
# ...
# Tailscale UDP
udp dport 41641 accept
# ...
}
# ============================================================
# FORWARD 链 (转发)
# ============================================================
chain forward {
# ...
iifname "tailscale0" accept
# ...
}
# ============================================================
# OUTPUT 链 (出站)
# ============================================================
chain output {
# ...
}
}
# 将来自 Tailscale 网段的流量进行源地址伪装
table inet tailscale_nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 100.64.0.0/10 masquerade
}
}
然后重载 Nftables 规则:
Debian:
sudo nft -c -f /etc/nftables.conf && sudo systemctl restart nftables; \
sudo systemctl restart docker; \
sudo systemctl restart fail2ban
Alpine:
sudo nft -c -f /etc/nftables.nft && sudo service nftables restart; \
sudo service fail2ban restart; \
sudo service docker restart
在 Headscale 服务器 上执行以下操作:
# 创建一个名为 eu 的用户空间
sudo headscale users create eu
# 查看用户空间 id,通常首个用户 ID 为 1
sudo headscale users list
# 生成一个有效期为 24 小时且可复用的预验证密钥 (Pre-Auth Key)
sudo headscale preauthkeys create -e 24h --reusable -u 1
在安装好 Tailscale 客户端的节点上执行设备注册:
sudo tailscale up --login-server https://headscale.your.domain --authkey <Pre-Auth-Key> --accept-dns=true --hostname <自定义主机名>
注册成功后,即可查看该节点在 Tailnet 虚拟内网 IP:
# IPv4 + IPv6
tailscale ip
# IPv4
tailscale ip -4
# IPv6
tailscale ip -6
按照上续步骤,我们可以将更多设备接入这个网络中,查看设备节点状态:
tailscale status
# 或在服务器端查看详细列表
sudo headscale nodes list
为了获得最佳体验,建议将地理位置接近的节点分配在同一用户空间(如本文的 eu 空间)。在理想的点对点连接下,节点间的通信延迟应保持在较低水平:
dejavu@ultravps-sp2:~$ tailscale status
100.64.0.3 ultravps-sp2 eu linux -
100.64.0.2 bero-xms eu linux -
100.64.0.1 netcup-micro eu linux active; direct [xxxx:xxxx:xxxx:xxx::1]:xxxx, tx 1177896 rx 6176272
测试节点间的响应速度:
dejavu@ultravps-sp2:~$ tailscale ping bero-xms
pong from bero-xms (100.64.0.2) via [xxxx:xxxx:xxxx:xxx::1]:xxxx in 7ms
dejavu@ultravps-sp2:~$ tailscale ping netcup-micro
pong from netcup-micro (100.64.0.1) via [xxxx:xxxx:xxxx:xxx::1]:xxxx in 10ms
若开启了 Magic DNS,也可以直接通过主机名进行访问:
dejavu@ultravps-sp2:~$ ping -c 4 bero-xmsdejavu.local
PING bero-xmsdejavu.local (100.64.0.2) 56(84) bytes of data.
64 bytes from bero-xmsdejavu.local (100.64.0.2): icmp_seq=1 ttl=64 time=6.99 ms
64 bytes from bero-xmsdejavu.local (100.64.0.2): icmp_seq=2 ttl=64 time=7.24 ms
64 bytes from bero-xmsdejavu.local (100.64.0.2): icmp_seq=3 ttl=64 time=7.21 ms
64 bytes from bero-xmsdejavu.local (100.64.0.2): icmp_seq=4 ttl=64 time=7.21 ms
--- bero-xmsdejavu.local ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 6.990/7.162/7.239/0.100 ms
dejavu@ultravps-sp2:~$ ping -c 4 netcup-microdejavu.local
PING netcup-microdejavu.local (100.64.0.1) 56(84) bytes of data.
64 bytes from netcup-microdejavu.local (100.64.0.1): icmp_seq=1 ttl=64 time=10.3 ms
64 bytes from netcup-microdejavu.local (100.64.0.1): icmp_seq=2 ttl=64 time=10.5 ms
64 bytes from netcup-microdejavu.local (100.64.0.1): icmp_seq=3 ttl=64 time=10.4 ms
64 bytes from netcup-microdejavu.local (100.64.0.1): icmp_seq=4 ttl=64 time=10.3 ms
--- netcup-microdejavu.local ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 10.297/10.377/10.534/0.093 ms
构建私有内网后可以利用其点对点 (Peer to Peer) 特性,安全实现跨服务器服务互联,以下是一个典型的自托管应用案例。
参考 2025 年优雅地自托管 RSS 服务 ,我们将分布在不同地理位置的服务器通过 Tailnet 连接起来,最终组合成 Miniflux + RSSHub + Warp Docker + Apprise 的服务整体。
我们需要先明确各服务在 Tailnet 中的角色分工:
查看接入设备的节点信息:
netcup-mikro:~$ tailscale status
100.64.0.1 netcup-micro eu linux -
100.64.0.2 bero-xms eu linux -
100.64.0.3 ultravps-sp2 eu linux idle, tx 106964 rx 100484
使用 Cloudflare Zero Trust 注册 Teams 设备。以下配置将 SOCKS5/HTTP 代理端口直接绑定在 Tailnet 的内网 IP 上,确保只有内网设备可以调用:
services:
warp:
image: caomingjun/warp:slim-2026.3.846.0-2.12.0
container_name: warp
restart: unless-stopped
networks:
- warp-tunnel
device_cgroup_rules:
- "c 10:200 rwm"
ports:
# 绑定 Tailnet 内网 IP
- "100.64.0.1:10480:1080/tcp" # SOCKS5
- "100.64.0.1:10480:1080/udp" # SOCKS5
- "100.64.0.1:10481:1081/tcp" # HTTP
- "100.64.0.1:10481:1081/udp" # HTTP
deploy:
resources:
limits:
memory: 1G
environment:
- WARP_SLEEP=4
- GOST_ARGS=-L socks5://:1080?udp=true -L http://:1081
# - REGISTER_WHEN_MDM_EXISTS=
# - WARP_LICENSE_KEY= # optional
# For Tailscale
# - WARP_ENABLE_NAT=1
- BETA_FIX_HOST_CONNECTIVITY=1
cap_add:
# Docker already have them, these are for podman users
#- MKNOD
#- AUDIT_WRITE
# additional required cap for warp, both for podman and docker
- NET_ADMIN
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv4.conf.all.src_valid_mark=1
# uncomment for nat
- net.ipv4.ip_forward=1
- net.ipv6.conf.all.forwarding=1
- net.ipv6.conf.all.accept_ra=2
dns:
- 1.1.1.1
- 8.8.8.8
- 2606:4700:4700::1111
- 2001:4860:4860::8888
volumes:
- ./data:/var/lib/cloudflare-warp
networks:
warp-tunnel:
name: warp-tunnel
driver: bridge
enable_ipv6: true
服务启动后,我们可以验证其连通性:
# 检查 Warp Docker 容器内
sudo docker exec -it warp curl --socks5-hostname 127.0.0.1:1080 https://cloudflare.com/cdn-cgi/trace
# 检查 Warp Docker 容器外
# 检查 IPv4 IP 出口
sudo docker run --rm --network warp-tunnel curlimages/curl -4 --socks5 warp:1080 ip.zsh.moe
# 检查 IPv6 IP 出口
sudo docker run --rm --network warp-tunnel curlimages/curl -6 --socks5 warp:1080 ip.zsh.moe
# 在 Tailnet 内其他设备上测试
curl -x socks5h://100.64.0.1:10480 https://cloudflare.com/cdn-cgi/trace
curl -x http://100.64.0.1:10481 https://cloudflare.com/cdn-cgi/trace
services:
rsshub:
image: diygod/rsshub:2026-04-01
container_name: rsshub
restart: unless-stopped
ports:
# 绑定 Tailnet 内网 IP
- "100.64.0.1:1200:1200"
environment:
NODE_ENV: production
CACHE_TYPE: redis
REDIS_URL: "redis://redis:6379/"
PUPPETEER_WS_ENDPOINT: "ws://browserless:3000"
PUPPETEER_REAL_BROWSER_SERVICE: "http://real-browser:3000"
PROXY_URI: "http://warp:1081"
PROXY_URL_REGEX: ".*"
ALLOW_ORIGIN: "*"
deploy:
resources:
limits:
memory: 512M
networks:
- rsshub
- warp-tunnel
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:1200/healthz"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
- redis
- browserless
real-browser:
image: ghcr.io/hyoban/puppeteer-real-browser-hono
container_name: rsshub-real-browser
restart: unless-stopped
networks:
- rsshub
- warp-tunnel
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 512M
browserless:
image: browserless/chrome
container_name: rsshub-browserless
restart: unless-stopped
ulimits:
core:
hard: 0
soft: 0
deploy:
resources:
limits:
memory: 512M
networks:
- rsshub
- warp-tunnel
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:8.6.2-alpine3.23
container_name: rsshub-redis
restart: unless-stopped
networks:
- rsshub
volumes:
- ./redis:/data
deploy:
resources:
limits:
memory: 128M
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 5s
networks:
rsshub:
name: rsshub
driver: bridge
enable_ipv6: true
warp-tunnel:
external: true
services:
apprise:
image: caronc/apprise:1.3.3
container_name: apprise
restart: unless-stopped
networks:
- apprise
ports:
# 绑定 Tailnet 内网 IP
- '100.64.0.3:8000:8000'
environment:
APPRISE_STATEFUL_MODE: simple
APPRISE_WORKER_COUNT: "1"
deploy:
resources:
limits:
memory: 256M
volumes:
- ./config:/config
- ./plugin:/plugin
- ./attach:/attach
networks:
apprise:
name: apprise
driver: bridge
enable_ipv6: true
在 Miniflux 的配置中,关键在于正确设置代理及排除名单,以确保访问内网服务时不经过 Warp 代理。
services:
miniflux:
image: miniflux/miniflux:2.2.18
container_name: miniflux
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
networks:
- postgres-alpine
environment:
# 指定实际的数据库连接信息
- DATABASE_URL=postgres://miniflux:miniflux@postgres-alpine/miniflux?sslmode=disable
- DATABASE_MIN_CONNS=1
- DATABASE_MAX_CONNS=40
- CREATE_ADMIN=1
- ADMIN_USERNAME=
- ADMIN_PASSWORD=
- RUN_MIGRATIONS=1
# 修改域名
- BASE_URL=https://rss.your.domain/
- CLEANUP_ARCHIVE_READ_DAYS=180
- CLEANUP_ARCHIVE_UNREAD_DAYS=365
- MEDIA_PROXY_MODE=all
# 允许内网调用服务必须设置为 1
- FETCHER_ALLOW_PRIVATE_NETWORKS=1
- INTEGRATION_ALLOW_PRIVATE_NETWORKS=1
- HTTPS=true
# 使用 Warp Docker 的 Tailnet 代理地址
- HTTP_PROXY=http://100.64.0.1:10481
- HTTPS_PROXY=http://100.64.0.1:10481
# 必须绕过 Tailnet 里面的:RSSHub、Apprise、Warp Docker 的 IP 地址、PostgreSQL 容器服务主机名
- NO_PROXY=100.64.0.1,postgres-alpine,localhost,127.0.0.1,rss.your.domain,100.64.0.3
- HTTP_CLIENT_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
- WEBAUTHN=true
deploy:
resources:
limits:
memory: 512M
services:
postgres:
# ...
# ...
networks:
postgres-alpine:
external: true
服务启动后,Miniflux 可通过内网地址与各服务通信:
http://100.64.0.1:1200http://100.64.0.3:8000在本文中,我们使用 Headscale + Tailscale 构建了一个小型私有网络,将分散在公网各处的自托管服务加入同一个 虚拟局域网 中。有效缩减了服务攻击面,同时也简化了多机协作下的资源与服务共享。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。