背景
家里 homelab 常见一堆没法或不想装 Tailscale 的设备:NAS、打印机、摄像头、内网管理面板、只监听局域网的 HTTP API 等。出门时笔记本/手机已在 Tailnet 里,希望能像在家一样访问 192.168.x.x,又不必把每个服务暴露到公网。
做法是 子网路由(Subnet Router):选一台常开、接在家里的机器当网关,把整段局域网「宣告」给 Tailnet 其它成员。
这和 Exit Node 不同——后者是让整机上网走对方出口,见 Tailscale Exit Node:让内网服务器经海外 VPS 稳定出境。访问家里 NAS 或内网 API 不需要开 Exit Node。
目标架构
CLIENT_TS_IP 笔记本 / 手机(Tailscale 客户端)
│
│ Tailscale 虚拟网
▼
GATEWAY_TS_IP 家里常开机器(子网网关)
│
│ 物理局域网 HOME_LAN_CIDR(示例 192.168.1.0/24)
▼
LAN_DEVICE_IP NAS / 打印机 / 内网服务等| 角色 | 说明 |
|---|---|
| 客户端 | 任意已登录同一 Tailnet 的设备 |
| 子网网关 | 与目标设备在同一物理网段、建议长期在线 |
| 局网目标 | 打印机、NAS、Docker 内网端口、管理后台等 |
访问方式:客户端直接访问 局域网 IP(如 http://LAN_DEVICE_IP:端口),不要把网关的 Tailscale IP 当成 HTTP 反代地址。
典型用途举例:
| 场景 | 访问方式 |
|---|---|
| NAS 管理 | https://LAN_DEVICE_IP:5001 |
| 内网 Git / CI | http://LAN_DEVICE_IP:3000 |
| SSH 到 Linux | ssh user@LAN_DEVICE_IP |
| 仅局域网 API | curl http://LAN_DEVICE_IP:8080/health |
第一步:在网关上宣告子网
以下以 macOS + Tailscale App 为例;Linux 思路相同(ip_forward + tailscale set --advertise-routes)。
1. 确认网关能访问目标
在网关机器上:
ping -c 3 LAN_DEVICE_IP不通先查网线 / Wi‑Fi、防火墙,与 Tailscale 无关。
2. 开启 IP 转发
macOS:
sudo sysctl -w net.inet.ip.forwarding=1
echo 'net.inet.ip.forwarding=1' | sudo tee -a /etc/sysctl.confLinux:
sudo sysctl -w net.ipv4.ip_forward=13. 宣告网段
把 192.168.1.0/24 换成你家的真实网段:
sudo /Applications/Tailscale.app/Contents/MacOS/Tailscale set --advertise-routes=192.168.1.0/24两点踩坑记录:
- Mac App 已连接时,用
set,不要用up——后者可能覆盖 App 已有参数,后台仍显示「This machine does not expose any routes」。 sudo tailscale可能command not found——sudo的 PATH 往往不含/usr/local/bin,请写完整路径(见上)。
4. 本地验证
/Applications/Tailscale.app/Contents/MacOS/Tailscale debug prefs | python3 -c "
import sys, json
d = json.load(sys.stdin)
print('AdvertiseRoutes:', d.get('AdvertiseRoutes'))
"期望输出含:['192.168.1.0/24'](你的网段)。
5. 管理后台批准(必做)
- 打开 Tailscale Admin → Machines
- 找到网关机器 → Edit route settings
- 勾选对应网段 → Save
未批准前,其它设备永远访问不到局域网 IP。
6. macOS 本地网络权限
系统设置 → 隐私与安全性 → 本地网络 → 为 Tailscale 打开权限,否则转发到局域网可能失败。
第二步:在客户端验证连通
/Applications/Tailscale.app/Contents/MacOS/Tailscale status | grep 192.168.1应出现类似:192.168.1.0/24 via <网关主机名>。
ping -c 3 LAN_DEVICE_IP
# 按服务类型继续测,例如:
curl -sI http://LAN_DEVICE_IP/
ssh user@LAN_DEVICE_IP能 ping 通、端口可达,子网路由即生效。
要不要开 Exit Node?
不需要。
| 能力 | 子网路由 | Exit Node |
|---|---|---|
| 访问局域网 IP | ✅ | ❌(无关) |
| 整机上网走对方出口 | ❌ | ✅ |
仅为访问家里局网设备,不要在客户端把网关选成 Exit Node,否则无关流量也会绕路。
故障排查速查
| 现象 | 可能原因 | 处理 |
|---|---|---|
sudo: tailscale: command not found | sudo PATH 不含 CLI | 使用 App 内二进制全路径 |
| 后台「does not expose any routes」 | 未 set 或未批准 | tailscale set --advertise-routes=... + Admin 勾选 |
| 客户端 ping 不通局域网 IP | 路由未批准;网关睡眠 | 批准网段;保持网关在线 |
| 网关通、某服务不通 | 目标防火墙 / 只监听 127.0.0.1 | 在网关本机先测该服务 |
| 误开 Exit Node | 与访问局网无关 | 客户端取消 Exit Node |
示例:局网 OpenAI 兼容 API(可选)
子网打通后,若家里还有 MLX / Ollama 反代 等只监听局域网的 LLM API,用法与其它内网 HTTP 服务相同:直接访问 http://LAN_DEVICE_IP:端口,无需为 Tailscale 单独开 Exit Node。
冒烟
curl -s http://LAN_DEVICE_IP:8082/v1/models
curl -s -X POST http://LAN_DEVICE_IP:8082/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "your-local-model-id",
"messages": [{"role": "user", "content": "说你好"}],
"stream": false,
"max_tokens": 100
}'根路径若只返回 JSON 元信息、没有聊天界面,属正常——需用 API 客户端(Cursor、ChatBox 等)。
客户端易踩坑(与 Tailscale 无关)
| 配置项 | 注意 |
|---|---|
| Base URL | http://LAN_DEVICE_IP:8082/v1(必须带 /v1) |
| Model | 与 /v1/models 返回的 id 完全一致 |
| 流式 | 部分代理首包 content: null,客户端可能空白 → 关闭 Stream |
curl 有正文、UI 空白时,优先查 API 配置,不是子网路由问题。
小结
- 选一台常在线、与目标设备同网段的机器做 Subnet Router。
- macOS 上
tailscale set --advertise-routes+ Admin 批准,比tailscale up更稳。 - 客户端访问 局域网 IP,不是网关的 Tailscale IP。
- 子网路由与 Exit Node 分工不同,不要混用。
- LLM、NAS 等只是局网上的具体服务,连通性验证通过后再排应用层配置。
延伸阅读
本文为个人 homelab 实践脱敏整理,文中
LAN_DEVICE_IP、HOME_LAN_CIDR、CLIENT_TS_IP等为示例占位,请按自己的环境替换。

讨论区
欢迎留下想法与补充