2026 frp 自建内网穿透:Docker 部署替代 ngrok 实现外网访问内网服务
你家里跑着 Jellyfin 影视库、Navidrome 音乐服务和一台 NAS,但在外面想访问它们的时候,被现实狠狠打了一拳——运营商没给公网 IP,你处在 CGNAT(运营商级 NAT)后面。
你试过 ngrok,免费版每分钟只让传 20 个请求,URL 还是随机的。你试过 Tailscale/ZeroTier,确实能连,但每个访问者都要装客户端。你甚至想过买一台有公网 IP 的 VPS 把所有服务搬上去,但钱包在抗议。
frp 就是来解决这个问题的。它是一个用 Go 语言编写的内网穿透代理服务器,GitHub 上 107k Star,是目前最流行的自建穿透方案。你只需要一台有公网 IP 的云服务器跑 frps(服务端),在家里的设备上跑 frpc(客户端),就能把内网服务安全地暴露到公网上。访问者不需要装任何东西,直接浏览器打开就能用。
而且 Go 语言的优势在这里体现得很明显——单二进制、零依赖、跨平台。不管你的 VPS 是 Linux 还是家里的树莓派、群晖 NAS,都能轻松跑起来。
一、frp 与其他穿透方案对比
| 对比项 | frp | ngrok | Tailscale | Cloudflare Tunnel |
|---|---|---|---|---|
| 语言 | Go | Go | Go | Go |
| 价格 | 免费开源 | 免费额度少 | 免费(个人) | 免费 |
| 自建可控 | ✅ 完全可控 | ❌ | ❌ 依赖协调服务器 | ⚠️ 依赖 Cloudflare |
| TCP/UDP 穿透 | ✅ 支持 | ❌ 仅 HTTP | ✅ | ❌ 仅 HTTP(S) |
| 客户端安装 | ❌ 不需要 | ❌ 不需要 | ✅ 需安装 | ❌ 不需要 |
| 配置灵活度 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 性能 | 高(Go 原生) | 中等 | 高(P2P 直连时) | 中等 |
简单结论: 如果追求极致灵活、完全可控,且需要 TCP/UDP 全协议支持,frp 是最优解。Tailscale 适合设备间互联,ngrok 适合临时测试,Cloudflare Tunnel 适合 HTTP 服务但受 CF 政策约束。
二、frp 架构
frp 有清晰的 C/S 架构:
公网 VPS(有公网 IP) ┌─────────────────┐ │ frps (服务端) │ │ 端口 7000 │ └────────┬────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ▼ ▼ ▼┌────────┐ ┌────────────┐ ┌──────────────┐│ frpc │ │ frpc │ │ 外部客户端 ││ (家里)│ │ (办公室) │ │ (用户浏览器) │└────────┘ └────────────┘ └──────────────┘ │ ▼┌────────────┐│ Jellyfin ││ Navidrome ││ SSH 服务 │└────────────┘工作流程:
- 家里的 frpc 主动连接 VPS 上的 frps(无需开放家庭端口)
- frps 在公网上监听指定端口
- 用户访问 VPS 的 IP:端口,frps 把流量转发到家里的 frpc
- frpc 再转发到对应的内网服务
三、Docker Compose 部署
3.1 服务端部署(frps — 跑在 VPS 上)
version: '3.8'
services: frps: image: snowdreamtech/frps:latest container_name: frps restart: unless-stopped network_mode: host volumes: - ./frps.toml:/etc/frp/frps.toml使用
network_mode: host是因为 frp 需要绑定大量随机端口,用 bridge 网络会非常麻烦。
创建 frps.toml 配置文件:
bindPort = 7000kcpBindPort = 7000 # 启用 KCP 加速quicBindPort = 7000 # 启用 QUIC 协议bindAddr = "0.0.0.0"
# 鉴权auth.token = "你的强密码请替换"
# Dashboard 监控面板webServer.addr = "0.0.0.0"webServer.port = 7500webServer.user = "admin"webServer.password = "你的管理密码请替换"
# 日志log.to = "console"log.level = "info"log.maxDays = 3
# 允许的最大连接数transport.maxPoolCount = 50
# 默认客户端端口范围(用于 TCP 穿透)vhostHTTPPort = 8080 # HTTP 穿透统一端口vhostHTTPSPort = 8443 # HTTPS 穿透统一端口3.2 客户端部署(frpc — 跑在家庭内网设备上)
version: '3.8'
services: frpc: image: snowdreamtech/frpc:latest container_name: frpc restart: unless-stopped network_mode: host volumes: - ./frpc.toml:/etc/frp/frpc.toml创建 frpc.toml 配置文件:
serverAddr = "你的VPS公网IP"serverPort = 7000auth.token = "和服务端一样的密码"
# ===== 代理配置 =====
# 穿透 SSH(22 端口)[[proxies]]name = "ssh"type = "tcp"localIP = "127.0.0.1"localPort = 22remotePort = 6000 # 访问 VPS:6000 → SSH 到家里的机器
# 穿透 Jellyfin(HTTP 服务)[[proxies]]name = "jellyfin"type = "http"localIP = "127.0.0.1"localPort = 8096customDomains = ["jellyfin.你的域名.com"]
# 穿透 Navidrome(HTTP 服务)[[proxies]]name = "navidrome"type = "http"localIP = "127.0.0.1"localPort = 4533customDomains = ["music.你的域名.com"]3.3 启动服务
# VPS 上启动 frpsdocker compose up -d
# 家里的机器上启动 frpcdocker compose up -d启动后访问 http://你的VPS_IP:7500 就能看到 frp Dashboard 监控面板了。
四、配置详解
4.1 代理类型
| 类型 | 用途 | 示例 |
|---|---|---|
tcp | 任意 TCP 服务 | SSH、RDP、数据库、Minecraft |
udp | 任意 UDP 服务 | DNS、游戏语音 |
http | Web 服务 | Jellyfin、Navidrome、Homepage |
https | HTTPS 服务 | 需配置证书 |
stcp | 安全 TCP(P2P 直连) | 仅 frpc 之间可访问 |
xtcp | P2P 穿透 | 两端都用 frpc,不经过服务器中转 |
4.2 安全配置
# frps.toml 安全相关auth.token = "your-strong-random-token"auth.additionalScopes = ["HeartBeats", "NewWorkConns"]
# TLS 加密(推荐开启)transport.tls.force = true
# 限制客户端 IPallowPorts = [ { start = 6000, end = 6005 }, { start = 7000, end = 7005 }]4.3 性能优化
transport.maxPoolCount = 100transport.tcpMux = true # TCP 多路复用
# frpc.toml 启用 KCP 加速transport.protocol = "kcp" # 使用 KCP 替代 TCP,丢包率高时效果明显KCP 协议在家庭网络丢包率高的情况下特别有效,可以显著降低延迟。如果网络质量好,默认的 TCP 协议就够用了。
五、实战场景
场景 1:SSH 远程管理家里的 Linux 机器
[[proxies]]name = "ssh-home"type = "tcp"localIP = "192.168.1.100"localPort = 22remotePort = 6000连接命令:ssh root@你的VPS_IP -p 6000
场景 2:暴露 Web 服务(配合 Caddy 反代)
先在 VPS 上给 frp 的 HTTP 穿透配一个 Caddy 反代:
jellyfin.你的域名.com { reverse_proxy localhost:8080 # frps 的 vhostHTTPPort}frpc 配置:
[[proxies]]name = "jellyfin-web"type = "http"localIP = "192.168.1.100"localPort = 8096customDomains = ["jellyfin.你的域名.com"]场景 3:游戏服务器穿透(Minecraft)
[[proxies]]name = "minecraft"type = "tcp"localIP = "192.168.1.100"localPort = 25565remotePort = 25565朋友直接连接 你的VPS_IP:25565,就像访问一台公网服务器一样。
六、Dashboard 监控
frp 内置了 Web Dashboard,启动后访问 http://你的VPS_IP:7500:
- 总览:在线客户端数、代理数、流量统计
- 代理状态:每个代理的在线状态、流量、延迟
- 客户端列表:连接的 frpc 详情
- 实时日志:查看连接日志
这对排查连接问题非常有用。
FAQ
Q: frp 和 Tailscale 有什么区别? A: 核心区别在于连接方式。Tailscale 走的是 P2P 直连(基于 WireGuard),需要双方都安装客户端;frp 走的是服务器中转,客户端不需要安装任何东西。frp 适合暴露服务给非技术人员使用,Tailscale 适合你自己的设备组网。
Q: VPS 需要什么配置? A: frps 极其轻量,1核1GB 内存的 VPS 跑 frps 同时穿透 10 个服务毫无压力。带宽决定了访问速度——建议至少 5Mbps。
Q: 安全吗? A: frp 支持 Token 鉴权、TLS 加密传输、IP 白名单。建议:① 设置强 Token ② 开启 TLS ③ Dashboard 不要暴露到公网 ④ 定期更新版本。
Q: HTTP 穿透和 TCP 穿透怎么选? A: Web 服务(Jellyfin/Navidrome/Gitea)用 HTTP 穿透 + 域名访问;非 HTTP 服务(SSH/Minecraft/RDP)用 TCP 穿透 + 端口映射。
Q: 可以用 frp 穿透 RDP 远程桌面吗?
A: 可以。用 TCP 类型穿透 3389 端口,然后在远程电脑上 mstsc /v:你的VPS_IP:remotePort 即可连接。
Q: frp 最新文档在哪里? A: 官方文档:https://gofrp.org/ 或直接看 GitHub 仓库 https://github.com/fatedier/frp (107k⭐)
Q: frp v2 有什么区别? A: frp v2 目前还在开发中(dev 分支),重构了配置格式,加入了更多企业级功能。目前生产环境建议使用 v1.x 稳定版。
frp 解决了自建服务”出不去”的核心痛点。下一篇我们来聊聊另一个 Go 写的反向代理神器——Traefik,它能自动发现 Docker 容器并自动配置 HTTPS。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!