前阵子把一台吃灰的树莓派4B翻出来,本来是想着拿来当下载机,结果发现远程办公的场景比下载更刚需。公司电脑、家里台式机、笔记本之间来回倒文件,试过TeamViewer、AnyDesk,要么被限速要么商业授权弹窗烦人。后来自己部署了RustDesk服务器,把这台树莓派变成了纯私有的中继节点,内网延迟只有几毫秒,外网连回家里的体验也稳了不少。这篇就聊聊从零到外网可用的完整过程,全程命令可以直接抄。
我默认你有最基础的树莓派使用经验,比如会烧录系统、会用SSH登录。下面从选型原因开始讲,因为只有理解了每个环节背后的逻辑,遇到问题时才知道往哪个方向排查。
1. 为什么选树莓派4B + RustDesk自建:延迟、隐私和成本三个维度说清楚
很多人习惯直接用云服务器装RustDesk Server,我的建议是:如果只是给自己家里或者小团队用,树莓派4B放在家里反而更合适。这不是省钱强迫症,而是有几个实打实的原因。
1.1 自建中继省掉的其实是“绕路”
RustDesk的工作逻辑是:两台设备先尝试P2P直连,如果打洞成功,数据完全不走服务器;打洞失败,才走服务器中继。你自建服务器之后,P2P成功的情况流量不经过任何第三方,中继的情况数据也只从你自家服务器过一遍。
用官方服务器时,A地设备连B地设备,信令包先跑到海外数据中心绕一圈再回来,延迟高不说,还受官方服务器负载影响。自建后,信令和中继链路完全掌握在自己手里,尤其是同一运营商网络下,时延能明显降下来。我实测下来,同城跨运营商中继延迟从官方节点的80-120ms降到20ms以内。
1.2 树莓派4B的硬件完全足够跑这套服务
RustDesk Server其实非常轻。整套服务拆开就是两个二进制进程:
- hbbs:ID注册和信令服务器,负责设备身份登记、建立会话、打洞协调
- hbbr:中继服务器,负责P2P失败后的数据转发
树莓派4B用的是BCM2711四核A72处理器,内存2GB/4GB/8GB版本都能跑。我手头是一台4GB版本,同时跑hbbs + hbbr + Docker + 一个文件同步脚本,内存占用大概400MB左右,CPU平时1%都不到。满载中继时CPU也基本在10%以内。功耗就更不用说了,整机加电源适配器大约5W,一年365天开机,电费不到一台云服务器一个月租。
1.3 跟TeamViewer和云服务器对比,优势在哪
我整理过一张对比表,帮当时纠结方案的时候捋清思路:
| 方案 | 典型成本 | 数据链路 | 主要短板 |
|---|---|---|---|
| TeamViewer/AnyDesk免费版 | 免费,但限制时长或弹窗 | 第三方服务器 | 商用检测、连接限速、不稳定 |
| 云服务器自建RustDesk | 轻量服务器月租约20-50元 | 你的云服务器 | 流量费另算,离你物理距离远 |
| 树莓派4B自建 | 一次性约300元,电费可忽略 | 完全私有 | 需要公网IP和端口映射 |
隐私维度很关键。RustDesk的通信是端到端加密,服务器只做信令和中继,理论上服务器管理员拿不到你的屏幕内容。但你自建服务器之后,这个“理论上”直接变成“事实上”,密钥在自己手里,数据链路在自己机器上,不需要信任任何第三方。如果做企业内部远程支持,这一点尤其重要,毕竟谁也不想让员工的远程桌面流量绕道海外。
2. 部署前的准备工作:系统镜像、固定IP、Docker环境一次配齐
部署最烦的不是执行命令,而是环境没准备好。我踩过一次系统镜像版本不对导致内核模块缺失的坑,所以这里把前置工作拆细。
2.1 系统镜像选择:官方Lite版就是最优解
树莓派官方系统(Raspberry Pi OS)Lite版本是无桌面环境的,资源占用极低,我用的就是这个。你也可以用Ubuntu Server 22.04/24.04的64位版本,两者的区别不大。选Lite版本还有个隐形好处:系统里没有桌面环境,就不会有人误打误撞打开浏览器占内存。
烧录直接用官方Raspberry Pi Imager工具,烧录前在设置里提前开启SSH服务,减少一次插屏幕接键盘的操作。烧录完成后,第一次开机建议用网线连路由器,这样系统能自动拿到IP,然后用路由器后台找到树莓派的地址,SSH进去。
ssh pi@树莓派IP默认用户名是pi,密码是raspberry,如果你用的是Imager的定制设置,用你自己设的用户名密码。
2.2 给树莓派设置固定IP
这一步看起来很基础,但特别影响后面外网配置。如果树莓派的IP是DHCP动态分配的,路由器重启或租约过期后IP一变,端口映射就全失效了。
最稳的方式是在路由器后台的“DHCP静态分配”里,把树莓派的MAC地址绑定到一个固定IP,比如192.168.1.100。也可以在树莓派系统里配置静态IP,两者选一个就行,不建议同时配置,否则容易出冲突。
树莓派官方系统改静态IP,编辑/etc/dhcpcd.conf:
sudo nano /etc/dhcpcd.conf在文件末尾加:
interface eth0 static ip_address=192.168.1.100/24 static routers=192.168.1.1 static domain_name_servers=192.168.1.1 223.5.5.5保存后重启网络:
sudo systemctl restart dhcpcd用ip addr确认IP变更为192.168.1.100。
2.3 安装Docker和docker-compose
RustDesk Server有两种跑法,一种是直接跑官方Shell脚本,一种是用Docker跑。我强烈推荐Docker,因为官方脚本会从源码编译或者从GitHub拉取二进制,耗时和出错概率都高,而Docker镜像开箱即用,升级也方便。
树莓派上安装Docker不要用官方的curl脚本,用系统自带的软件源最省心:
sudo apt update sudo apt install -y docker.io docker-compose-v2 sudo usermod -aG docker $USER装完后重新登录SSH,让用户组权限生效。验证一下:
docker --version docker compose version看到版本号就说明环境好了。这里有个细节:现在的docker-compose命令是docker compose(带空格),不是老的docker-compose(带横线),网上很多教程还在用横线版,在新版Docker里可能会提示找不到命令。
3. 部署RustDesk Server:我实际跑通的Docker Compose方案
3.1 为什么不直接用官方install.sh脚本
RustDesk官方提供了install.sh一键安装脚本,思路是下载预编译的hbbs和hbbr二进制文件。看起来很方便,但实际用下来有几个问题:脚本默认从GitHub拉取文件,网络波动大时容易下载失败;装完没有systemd服务管理,重启树莓派后不会自动拉起;升级时要重新跑脚本,比较折腾。
我用Docker Compose方案,把hbbs和hbbr定义成两个容器,通过host网络模式直接复用树莓派物理网卡,省去端口映射容器端口的环节。
3.2 编写docker-compose.yml
先创建工作目录:
mkdir -p ~/rustdesk-server && cd ~/rustdesk-server创建docker-compose.yml:
nano docker-compose.yml内容如下:
services: hbbs: image: rustdesk/rustdesk-server:latest container_name: hbbs command: hbbs -r your-server.com:21117 volumes: - ./data:/root network_mode: host restart: unless-stopped hbbr: image: rustdesk/rustdesk-server:latest container_name: hbbr command: hbbr volumes: - ./data:/root network_mode: host restart: unless-stopped解释几个关键点:
command: hbbs -r your-server.com:21117这里的-r参数告诉hbbs,向客户端广播的中继服务器地址是your-server.com:21117。这一步非常关键,如果填写的地址不对,客户端即使P2P直连成功,也无法获得正确的中继地址。还没申请域名的,先填“当前树莓派的外网IP:21117”,后面配置DDNS后再改。network_mode: host让容器直接使用宿主机网络,RustDesk涉及UDP打洞和多端口通信,host模式最省心,不会有NAT转换问题。./data:/root持久化密钥文件。hbbs启动后会在/root目录下生成id_ed25519和id_ed25519.pub,这是客户端接入时要填的Key,必须持久化,否则容器重建后Key变了,所有客户端要重新配置。restart: unless-stopped保证树莓派重启后两个服务自动拉起,不需要手动干预。
3.3 启动并验证服务状态
docker compose up -d docker compose ps看到两个容器状态都是Up就成功了一半。再来确认密钥文件已经生成:
ls -l data/ cat data/id_ed25519.pubcat出来的字符串就是后面客户端要用的Key,先复制保存好。我建议顺手把私钥id_ed25519也备份一份,后面换机器迁移服务器时能省大事。
到这里,内网部署已经完成。此时你可以在局域网内打开RustDesk客户端,把ID服务器填成192.168.1.100,Key填成刚复制的公钥,应该就能连上。如果你只在家里用,到这里就够了。但要做外网访问,还差最后一截路。
4. 客户端接入与验证:让所有设备都走自己的服务器
部署完服务端,下一步就是把电脑、手机上的RustDesk客户端指向你自己的服务器。
4.1 桌面端配置流程
以Windows客户端为例,打开主界面,点击右上角菜单(三条横线),选择“设置”,切到“网络”标签页。在“ID/中继服务器”一栏里,点右侧的“解锁”按钮(防止误改),然后把局域网内测试写的192.168.1.100和Key填进去:
- ID服务器:192.168.1.100
- 中继服务器:留空或填192.168.1.100:21117
- Key:刚才cat出来的那一串
填完点“应用”,状态栏应该会从“未就绪”变成“就绪”。
手机端(Android/iOS)操作类似,在“设置 -> 网络”里填同样的内容。iPhone上需要对RustDesk授权本地网络权限,否则会一直显示连接中,这个问题我周围好几个朋友都遇到过。
4.2 怎么确认流量走了自己的服务器
填完之后不要急着连,先在树莓派上看日志,确认客户端确实注册到了你的服务器上:
docker logs -f hbbs正常会看到类似这样的记录:
[INFO] new connection from 192.168.1.50:50001 [INFO] register peer: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx如果看到这种输出,说明客户端成功连上了你的hbbs。之后再进行一次远程桌面连接,如果日志里有hbbr的连接记录,说明P2P直连没打通,走了中继;如果只有hbbs的信令记录,说明打洞成功,数据完全直连。
4.3 端口清单,哪些必须开
先明确一下RustDesk Server需要哪些端口,这会影响你后面的防火墙和路由器配置:
| 端口 | 协议 | 用途 | 是否必须 |
|---|---|---|---|
| 21115 | TCP | NAT类型探测 | 推荐打开 |
| 21116 | TCP | 客户端注册、心跳 | 必须 |
| 21116 | UDP | 打洞、P2P协商 | 必须 |
| 21117 | TCP | 中继服务 | 必须 |
| 21118 | TCP | Web客户端 | 非必须 |
| 21119 | TCP | Web客户端 | 非必须 |
我自己实际只放开了21115、21116和21117。如果你不需要浏览器远程,21118和21119不映射也没问题。
5. 外网访问的完整链路:端口映射、DDNS与安全加固
树莓派在家里,外面要能连上,本质上就是“让外网能访问到树莓派的特定端口”。这一步有四个环节,任何一个断了都不行。
5.1 先确认有没有公网IP
登录路由器管理页面,查看WAN口的IP地址。如果WAN口IP是公网地址(不是192.168.x.x、10.x.x.x、172.16-31.x.x这些私网段),那恭喜你,可以直接做端口映射。如果WAN口IP是私网段,说明运营商没有给你分配公网IPv4,这种环境就要想别的办法。
比较常见的替代方案:
- 运营商下发IPv6地址:树莓派和客户端都有IPv6地址时,直接把IPv6地址填到客户端的ID服务器里,也能实现外网访问。前提是路由器、光猫和系统防火墙都要放行对应端口。
- 用组网工具(如Tailscale、ZeroTier)把树莓派和外部设备组成一个虚拟局域网,然后直接用虚拟局域网IP访问。这种方式适合“只有自己设备访问”的场景,配置简单,不需要公网IP,但如果你要把访问权分享给朋友或同事,就不太合适。
这部分在实际部署中最容易劝退人。我自己的情况是运营商分配了动态公网IPv4,但IP会变,所以后面必须上DDNS。
5.2 光猫和路由器的端口映射
大部分家庭网络是“光猫-路由器-电脑”的结构。做端口映射时,优先在路由器上做,但也要注意光猫如果处于路由模式,会拦截转发。最彻底的方案是把光猫改成桥接模式,让路由器来拨号,这样端口映射只在路由器上做一次。光猫桥接的设置需要找运营商要超级管理员密码,不同地区不太一样,这里不展开。
路由器后台找到“端口映射”或“虚拟服务器”功能,添加如下映射规则,把公网端口转发到树莓派内网IP 192.168.1.100:
| 外部端口 | 内网IP | 内部端口 | 协议 |
|---|---|---|---|
| 21115 | 192.168.1.100 | 21115 | TCP |
| 21116 | 192.168.1.100 | 21116 | TCP |
| 21116 | 192.168.1.100 | 21116 | UDP |
| 21117 | 192.168.1.100 | 21117 | TCP |
保存后,从外网用公网IP:21116测一下端口通不通。Windows上可以在本机用Test-NetConnection,或者用在线端口检测工具。如果测不通,先查路由器的防火墙规则,再查树莓派自己的ufw状态。
树莓派上如果开了系统防火墙ufw,放行命令:
sudo ufw allow 21115/tcp sudo ufw allow 21116/tcp sudo ufw allow 21116/udp sudo ufw allow 21117/tcp sudo ufw reload5.3 DDNS绑定域名,解决动态IP问题
动态公网IP意味着今天可能是100.64.1.10,明天变成100.64.1.15。手动查IP再填到客户端里,太原始。用DDNS把一个域名动态绑定到你当前的IP上,一劳永逸。
DDNS有三种实现路径:
- 路由器自带DDNS功能:多数路由器支持Dyndns、花生壳、阿里云域名等,直接在路由器后台填账号密码就完事。
- 树莓派上跑inadyn:如果你的路由器不支持特定服务商,可以在树莓派上装inadyn:
sudo apt install inadyn配置示例(以DuckDNS为例):
sudo nano /etc/inadyn.confprovider duckdns { username = "你的token" password = "no-password-needed" hostname = "你的域名.duckdns.org" ddns-server = "www.duckdns.org" ddns-path = "/update?domains=mydomain&token=xxx&ip=" update-period = 600 }- 利用域名服务商(如Cloudflare)的API,在定时任务里更新DNS记录。这个适合已注册自有域名、希望完全掌控解析的人。
配置好DDNS后,用ping 你的域名确认解析到的IP就是当前公网IP,再把RustDesk服务端命令里的-r参数从IP改成域名:
command: hbbs -r your-server.com:21117然后:
docker compose up -d客户端那边清除旧的ID服务器,重新填域名即可。
5.4 安全加固:改端口、最小化暴露、管好Key
RustDesk默认端口是公开规律,公网扫描器经常扫这些端口。如果直接把默认端口暴露在公网,一天下来防火墙日志里全是尝试连接的记录。我建议做三层加固。
第一,改默认端口。在docker-compose.yml里把端口换成不常用的高位端口。hbbs的监听端口用-p指定,中继端口hbbr也用-p指定,例如:
services: hbbs: command: hbbs -r your-server.com:22117 -p 22116 hbbr: command: hbbr -p 22117对应的路由器端口映射也要全部改用22115、22116、22117。客户端ID服务器填your-server.com:22116,Key不变。
第二,只做必要的最小化映射。前面表格里我说过,21115只是NAT探测,21118/21119只给Web端用,如果你不用这些功能,就不要映射。少暴露一个端口,就少一份被扫描的风险。
第三,管好Key。RustDesk客户端接入需要填入id_ed25519.pub的公钥,这个Key就是你的门禁。不要把它贴在公共频道或发给不相干的人。万一Key泄露,重装服务端重新生成密钥并重置客户端,比换端口更有效。
6. 踩坑记录:我部署过程中遇到过的五个问题及解决过程
这个部分是我最想写的,因为我在部署过程中几乎把能踩的坑都踩了一遍。下面按排查链路整理,你照着顺序查就能省下半天时间。
6.1 客户端一直“连接中”,日志显示Failed to connect to rendezvous server
现象:内网都连不上,hbbs日志不断报失败。最终定位到树莓派系统防火墙ufw默认阻止了21116端口。当时我还没执行ufw放行命令,以为局域网内访问不需要防火墙,实际上一旦ufw enable,入站规则就会拦截所有非白名单端口。
排查思路:先在树莓派本机用ss -lunp | grep 21116确认hbbs在监听,再用curl或nc从另一台内网机器测端口:
nc -vz 192.168.1.100 21116如果超时,八成是防火墙问题。ufw status查看规则,放行端口后再测。
6.2 内网通,外网连不上,但外网端口检测显示已开放
现象:用在线工具检测21116/21117端口,显示已经开放,但手机端用4G网络无法连接。
这个坑非常隐蔽:运营商分配的“公网IP”并不是真正的公网IP,家庭宽带很多时候分配的是100.64.0.0/10段地址(CGNAT),表面看IP不是192.168开头,但实际上你仍然处于运营商的内部网络。在线端口检测工具检测的是你的出口IP,检测结果看起来通,但实际上数据包根本到达不了树莓派。
解决方式:换一个方式验证真实公网属性。在路由器后台把WAN口IP抄下来,用手机流量ping一下这个IP,能通才有戏。如果是100.64.x.x,基本可以确定是CGNAT,只能找运营商开公网IPv4,或者走IPv6、组网工具方案。
6.3 外网能连上但特别卡,画面一直转圈
现象:外网远程桌面能连,但操作延迟高达1-2秒,画质模糊。
查了半天,最后发现是路由器端口映射里漏了UDP 21116,只映射了TCP。RustDesk的打洞协商依赖UDP 21116,UDP端口不通时,客户端直接放弃P2P直连,所有流量全部走TCP中继。中继转发本身有损耗,跨运营商网络后延迟就堆上去了。
把UDP 21116也映射到树莓派后,连接质量立即好转。这个案例说明,端口映射协议类型一定要跟服务端对齐,TCP和UDP不是一个概念。
6.4 客户端Key报错,提示“Key不匹配”
现象:换了台电脑在新客户端填了ID服务器,但状态一直显示未连接,或者连接时提示Key错误。
我排查后发现是自己复制失误:当时从服务器上cat公钥,不小心把结尾的换行符也复制进去了,客户端粘贴时多了一个空行,导致密钥校验失败。
处理方式:把公钥内容复制到文本编辑器里,确认首尾没有空格和换行,再粘贴到客户端。另一个常见错误是复制了id_ed25519(私钥)而不是id_ed25519.pub(公钥),这两个文件都在data目录里,别搞混。
6.5 树莓派运行一段时间后远程桌面中间断连
现象:跑了一周左右,某天突然连不上,去路由器后台看,树莓派IP还是通的,但RustDesk服务挂了。重启后能恢复,但过几天又挂。
定位后发现是SD卡的问题。树莓派长时间频繁写入,低质量SD卡容易出现I/O错误,Docker容器写日志时触发文件系统异常,服务进程被卡死。我换了一张A2速度等级的工业级SD卡,并把Docker的data目录挂到一个U盘上,问题才彻底解决。
这里给长期运行用户一个补充建议:在fstab里为/home/pi/rustdesk-server/data配置noatime挂载参数,减少不必要的写入次数。RustDesk服务本身写入不频繁,主要是系统日志和Docker日志在持续写盘,定期清理日志可以延长SD卡寿命。
6.6 性能实测数据
最后分享一组我的实测数据,帮你建立一个心理预期:
| 场景 | 延迟 | 体验 |
|---|---|---|
| 内网P2P直连 | 1-3ms | 完全感觉不到延迟 |
| 外网同城、P2P成功 | 3-8ms | 操作基本同步 |
| 外网同城、走中继 | 15-25ms | 偶尔有轻微粘滞感 |
| 外网跨省、走中继 | 30-60ms | 能完成任务,但不适合看视频 |
如果你发现自己长时间走中继而不是P2P,可以检查客户端双方的网络类型、路由器UPnP是否开启、UDP端口是否通畅。做这一步优化,比换更贵的服务器更有效。
最后分享一个我踩了坑之后形成的习惯:把data目录下的id_ed25519和id_ed25519.pub备份到U盘或网盘。后来有一次我把树莓派换成别的型号,直接把data目录整体拷过去,新服务器起来后输入原Key,所有客户端的配置一次都不用改。如果你也准备长期跑这套服务,建议每三个月重启一次树莓派,把碎片清理一下,顺手备份密钥。先说到这儿,祝你外网访问配置一次通过。