Skip to content

Tailscale Exit Node:让内网服务器经海外 VPS 稳定出境

仲灏2026-06-24约 1 分钟

背景

家里 homelab 跑了一堆 Docker 服务(CI、Git、数据库等),日常需要拉 GitHub 镜像、访问 Docker Hub、下载海外依赖。单独再搭一套 Clash / sing-box 客户端固然可行,但维护成本高,而且和已有 Tailscale 组网重复。

更轻量的思路:海外本来就有一台能访问全球网络的轻量 VPS(记为 OVERSEAS),国内 Linux 服务器(记为 HOME)也在同一 Tailnet 里——直接把 OVERSEAS 配成 Exit Node(出口节点),HOME 的全局流量经加密隧道从海外出去即可。

本文记录完整落地步骤,以及一个极易踩坑的 Docker + nftables 转发 问题。


目标架构

text
┌─────────────────────┐      Tailscale 隧道       ┌──────────────────────┐
│  HOME(国内 homelab)  │ ────────────────────────► │  OVERSEAS(海外 VPS) │
│  HOME_TAILSCALE_IP   │   自建 DERP 中继或直连     │  OVERSEAS_TS_IP      │
│  原宽带出口           │                           │  海外公网 IP          │
└─────────────────────┘                           └──────────┬───────────┘
         │ exit-node 启用后                                      │
         │ 默认外连经 OVERSEAS 转发                                 ▼
         └──────────────────────────────────────────────► 全球互联网
角色说明
OVERSEAS海外 VPS,宣告 Exit Node,负责 NAT 出境
HOME国内服务器,指定 --exit-node 指向 OVERSEAS
DERP两端无法 UDP 直连时走中继;见下文「DERP 别搞混」

和「Nginx 反代桥」的区别:反代桥解决的是「外网用户访问内网服务」;Exit Node 解决的是「内网机器主动访问外网」。方向相反,不要混用方案。


前置条件

  • HOME 与 OVERSEAS 已加入同一 Tailnettailscale status 互见)
  • 两端建议用 Docker 跑 tailscale/tailscalenetwork_mode: host
  • OVERSEAS 开启 IP 转发:
bash
# /etc/sysctl.d/99-tailscale.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

步骤一:海外机宣告 Exit Node

docker-compose.yml 核心片段:

yaml
services:
  tailscale:
    image: tailscale/tailscale:latest
    container_name: tailscale
    hostname: overseas-exit
    network_mode: host
    environment:
      - TS_AUTHKEY=${TS_AUTHKEY}
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_HOSTNAME=overseas-exit
      - TS_USERSPACE=false
      - TS_EXTRA_ARGS=--advertise-exit-node
    volumes:
      - ./tailscale-state:/var/lib/tailscale
    devices:
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - net_admin
      - sys_module
    restart: unless-stopped

TS_AUTHKEYTailscale Keys 生成,写入 .env不要提交到 Git


步骤二:管理后台批准(必做)

仅宣告不够,必须在控制台手动批准:

  1. 打开 Machines
  2. 找到海外机 → Edit route settings
  3. 勾选 Use as exit node → 保存

未批准时,客户端会报:

text
node OVERSEAS_TS_IP is not advertising an exit node

批准后可用 CLI 确认:

bash
tailscale exit-node list
# 应出现 OVERSEAS_TS_IP  overseas-exit.<tailnet>.ts.net

步骤三:国内机指定出口

docker-compose.yml

yaml
environment:
  - TS_AUTHKEY=${TS_AUTHKEY}
  - TS_STATE_DIR=/var/lib/tailscale
  - TS_USERSPACE=false
  - TS_EXTRA_ARGS=--exit-node=OVERSEAS_TS_IP --exit-node-allow-lan-access

--exit-node-allow-lan-access 很重要:走出口时仍可访问家里局域网(如 192.168.x.x)。

临时切换(不改 compose):

bash
docker exec tailscale tailscale set --exit-node=OVERSEAS_TS_IP --exit-node-allow-lan-access=true

# 恢复国内直连
docker exec tailscale tailscale set --exit-node=

验证:

bash
curl -4 ifconfig.me          # 应显示海外 VPS 公网 IP
curl -s https://api.github.com/zen

步骤四:桌面端(Mac / Windows / 手机)

Linux 服务器可用 compose 持久化 --exit-node;笔记本、手机一般用官方客户端菜单临时切换(重启后常需重选)。

Mac

先处理代理冲突:若本机还开着 Clash Verge、Surge 等 TUN / 系统代理,流量会被它们先接走,Tailscale Exit Node 选了也不生效。要用 VPS 出境,须退出 Clash 或关闭 TUN

菜单栏操作

  1. 点右上角 Tailscale 图标
  2. Exit Node(或「使用出口节点」)
  3. 选择海外机(如 overseas-exit / OVERSEAS_TS_IP
  4. 打开 Allow LAN access(走出口时仍可访问家里 192.168.x.x

命令行(等价)

bash
tailscale set --exit-node=OVERSEAS_TS_IP --exit-node-allow-lan-access=true
tailscale set --exit-node=    # 恢复直连

验证:浏览器打开 ifconfig.me,应显示海外 VPS 公网 IP。

Windows

  1. 系统托盘 TailscaleExit Node
  2. 选择海外机,勾选 Allow local network access
powershell
tailscale set --exit-node=OVERSEAS_TS_IP --exit-node-allow-lan-access=true

同样需先关闭 Clash / Surge 等 TUN 客户端,否则与 Mac 一样不会走 Exit Node。

iOS / Android

Tailscale App → Exit Node → 选择海外机 → 开启 Allow LAN access(若有)。

桌面端 vs 服务器

项目Linux 服务器Mac / Win / 手机
持久化TS_EXTRA_ARGS 写进 compose菜单切换,重启后需重选
与 Clash 共存通常无冲突不可与 TUN 代理同时当默认出口
CI / 脚本可删旧 HTTP_PROXY,统一走 Exit Node不适用

若曾为 Gitea Actions 等配置过 HTTP_PROXY 指向桌面 Clash,Exit Node 落地后应删掉,避免 Mac 关机时 CI 断网。


DERP 别搞混

这是本文最想强调的一点,排障时容易误判。

类型说明
本方案实际走的Tailnet ACL 里配置的自建 DERPderpMap + OmitDefaultRegions: true),不走 Tailscale 官方默认中继
同机可能还跑着海外 VPS 上另有 derper 容器(如 derp.example.com),若不在 ACL derpMap,Exit Node 链路不会走它
也不是derpN.tailscale.com 等官方默认区域(已被 OmitDefaultRegions 关掉时)

排障时 tailscale status 可能显示 relay "region-a" 之类——那是 ACL 里登记的那台 DERP,不等于海外 VPS 本机 compose 里的 derper 服务。

局域网内有时能 direct 穿透,那是 bonus;跨境多数时候仍经你配置的 DERP。


踩坑:Docker 的 nftables 吃掉 Exit Node 转发

现象

  • 控制台已批准 Exit Node
  • tailscale set --exit-node=... 无报错
  • ping OVERSEAS_TS_IP 正常
  • ping 8.8.8.8curl ifconfig.me 超时

原因

海外 VPS 同时跑 Docker 时,nftables 的 FORWARD 链默认 policy=drop;而 Tailscale 安装的 ts-forward / ts-postrouting 规则在 iptables-legacy 里。包在 legacy 侧计数增加,但真正转发被 Docker 的 nft 规则丢掉,MASQUERADE 计数一直是 0。

修复

DOCKER-USER 链放行 tailscale0,并给 Tailscale 地址段做 NAT:

bash
#!/bin/bash
# /opt/compose/tailscale/fix-exit-node-iptables.sh

iptables -C DOCKER-USER -i tailscale0 -j ACCEPT 2>/dev/null || \
  iptables -I DOCKER-USER 1 -i tailscale0 -j ACCEPT
iptables -C DOCKER-USER -o tailscale0 -j ACCEPT 2>/dev/null || \
  iptables -I DOCKER-USER 1 -o tailscale0 -j ACCEPT
iptables -t nat -C POSTROUTING -s 100.64.0.0/10 -o eth0 -j MASQUERADE 2>/dev/null || \
  iptables -t nat -I POSTROUTING 1 -s 100.64.0.0/10 -o eth0 -j MASQUERADE

eth0 换成你海外机真实外网网卡名。建议做成 systemd oneshot,在 docker.service 之后执行,避免重启后规则丢失。

诊断时可对照:

bash
nft list chain ip filter FORWARD          # 常见 policy drop
iptables-legacy -L ts-forward -n -v       # legacy 侧有计数
iptables -t nat -L POSTROUTING -n -v      # 修复后 MASQUERADE 应增长

故障速查

现象可能原因处理
not advertising an exit node控制台未批准勾选 Use as exit node
ping 海外机通、外网不通Docker nft FORWARD 丢包执行上文 iptables 脚本
公网 IP 仍是国内宽带exit-node 未生效tailscale debug prefsExitNodeID
走出口后访问不了内网 NAS未开 LAN access--exit-node-allow-lan-access
桌面选了 Exit Node 但 IP 不变Clash / Surge TUN 仍启用先关 TUN 或退出客户端
速度慢经 DERP 中继、海外机带宽小tailscale ping 看 direct/relay;大文件优先国内镜像

运维建议

  • apt / Docker 镜像仍优先配国内源;Exit Node 适合 GitHub、海外 registry 等「必须出境」的场景,别拿 1C0.5G 小机器拉超大镜像。
  • 不要在公网裸奔无认证 SOCKS5;Exit Node 加密 + Tailscale 身份,更安全。
  • Auth Key、root 密码放环境变量或密码管理器,别写进博客或 compose 仓库。
  • Docker 大版本升级或 iptables 被重置后出口再断,先重跑 fix-exit-node-iptables.sh

和代理客户端方案怎么选

方案适合
Tailscale Exit Node已有 Tailnet、要整机一致出境、机器数量少
sing-box / mihomo + 节点要按域名分流、无 Tailscale
SSH -D临时验证链路

若你已经在用 Tailscale 做内网穿透或 L4 反代(可参考同目录 小内存海外 VPS 跑 NPM 卡死的迁移实践),Exit Node 是「出站」方向的自然延伸,无需再叠一套代理栈。


小结

  1. 海外机 --advertise-exit-node + 控制台批准
  2. 国内机 --exit-node=OVERSEAS_TS_IP --exit-node-allow-lan-access
  3. 海外机若跑 Docker,务必处理 nft FORWARD 与 tailscale0 的冲突
  4. DERP 以 Tailnet ACL 为准,别被同机多余的 derper 容器误导
  5. Mac / Win / 手机:菜单选 Exit Node;与 Clash TUN 二选一;删旧 HTTP_PROXY

按以上步骤,curl ifconfig.me 显示海外 IP、GitHub 可访问,即表示链路正常。

本文为个人 homelab 实践脱敏整理,文中 IP、域名、路径均为示例占位,请按自己的环境替换。

讨论区

欢迎留下想法与补充