SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor

RustDesk 自托管中继服务器部署指南

在 VPS 上运行 RustDesk hbbs 和 hbbr,了解 Ed25519 密钥、固定镜像标签、21115至21119端口、防火墙限制,以及中继流量如何计费。

自托管 RustDesk 中继服务器是什么

自托管 RustDesk 中继服务器是在一台 VPS 上运行的两个守护进程。hbbs 是 ID 和会合服务器:它注册每个客户端 ID,并让两个客户端彼此建立连接。hbbr 是中继服务器:它传输会话数据,但只处理中继连接无法直接建立的会话。大多数指南会安装这两个组件,帮助你完成配对,然后就此结束。下面介绍剩余工作:作为访问控制的密钥、端口、升级和带宽。

两个守护进程都包含在同一个镜像 rustdesk/rustdesk-server 中。在下面的配置中,两个进程都从同一目录读取同一对 Ed25519 密钥。该行为需要由你配置,并非默认设置:hbbs 会自行加载或生成密钥对,但 hbbr 只有在启动时指定密钥才会使用密钥。Ed25519 是一种公钥签名方案。该密钥对决定你的服务器会与哪些客户端通信,系统背后没有用户数据库。

hbbs 和 hbbr:消耗带宽的是哪个守护进程

hbbs 的流量少且稳定:包括 ID 注册、心跳,以及用于建立两个对等端连接的简短通信。它全天运行,几乎不会产生带宽成本。

hbbr 的流量就是会话本身。屏幕帧向一个方向传输,键盘和鼠标输入向另一个方向传输。每个经过中继的字节都会先到达您的 VPS,然后再次从 VPS 发出。如果服务商只统计出站流量,中继会话产生的费用大约等于会话速率对应的流量费用。如果服务商统计总传输量,费用大约是前者的两倍。

中继是备用路径,不是正常路径。hbbs 会首先尝试让两个客户端直接连接,并通过客户端各自所在网络前的 NAT(网络地址转换)执行打洞。连接成功后,会话不会经过 hbbr,也不会消耗您的传输配额。如果一方位于按目标地址分配新端口的 NAT 后,或防火墙丢弃打洞建立的路径,会话就会回退到 hbbr,每个帧都会经过您的 VPS。

一个环境变量会取消这一选择。ALWAYS_USE_RELAY=Y 在 hbbs 上强制所有会话经过 hbbr。RustDesk 文档在其中一个 Compose 示例中展示了该变量,因此它经常被直接复制。这样可以让连接更可预测,也会让出站流量真正产生。只有在您确实决定这样配置时才设置它,不要因为复制粘贴了示例就设置。

自托管 RustDesk 服务器需要哪些端口

以下端口号已根据 RustDesk 服务器文档和 rustdesk-server 代码仓库,于 10 October 2026 进行核对。

  • TCP 21115,在 hbbs 上:NAT 类型测试。
  • UDP 21116,在 hbbs 上:ID 注册和心跳。这是客户端默认使用的路径。
  • TCP 21116,在 hbbs 上:为仅使用 TCP 的客户端进行 ID 注册,同时用于 TCP 打洞和连接服务。
  • TCP 21117,在 hbbr 上:中继端口。会话数据通过此端口传输,因此该端口会产生流量费用。
  • hbbs 上的 TCP 21118 和 hbbr 上的 TCP 21119:WebSocket,供浏览器客户端使用。如果不使用浏览器客户端,请将这两个端口都保持关闭。
  • TCP 21114 是 RustDesk Server Pro 中的 HTTP API 服务器端口。开源版本不会监听此端口。

固定镜像标签安装 hbbs 和 hbbr

services:
  hbbs:
    container_name: hbbs
    image: rustdesk/rustdesk-server:1.1.16
    command: hbbs
    volumes:
      - ./data:/root
    network_mode: "host"
    depends_on:
      - hbbr
    restart: unless-stopped

  hbbr:
    container_name: hbbr
    image: rustdesk/rustdesk-server:1.1.16
    command: hbbr -k _
    volumes:
      - ./data:/root
    network_mode: "host"
    restart: unless-stopped
mkdir -p ~/rustdesk
cd ~/rustdesk
# save the block above as ~/rustdesk/compose.yml before this line
sudo docker compose up -d
sudo docker compose ps

两个服务都应读取 running。在操作防火墙之前,先确认监听端口已存在。

sudo ss -tlnp | grep -E ':2111[5-7]'
sudo ss -ulnp | grep ':21116'

您应看到 TCP 监听端口 21115、21116 和 21117,以及一个 UDP 监听端口 21116。缺少 UDP 监听项表示 hbbs 未运行,因为 hbbs 启动时会打开该监听端口,客户端默认通过它注册。

该文件中有 4 项设置是有意这样配置的。标签为 1.1.16,截至 October 2026 仍是当前版本,发布时间为 20 July 2026,而不是 latest,因为 latest 表示最近推送的任意版本,六个月后的 docker compose pull 可能会向您提供一个从未测试过的服务器。network_mode: "host" 直接绑定主机接口,这是 RustDesk 文档推荐的方式,也决定了防火墙的行为。./data:/root 将镜像工作目录映射到主机,因此密钥对会保存到可备份的位置。hbbr -k _ 是相对于上游示例唯一的修改,因为默认配置会让您的中继服务对任何人开放。如果您刚开始使用 Compose,在 VPS 上运行 Docker Compose介绍了文件格式和生命周期命令。

如果 hbbr 以后迁移到第二台服务器,则必须告知 hbbs 新位置:传递 -r relay.example.com:21117,或设置 RELAY-SERVERS 环境变量。在单台服务器上无需设置此项。

Ed25519 密钥对用于访问控制

首次启动时,hbbs 会在其工作目录中生成 id_ed25519 和 id_ed25519.pub。使用上面的挂载配置后,这两个文件都会出现在主机上。

ls -l ~/rustdesk/data/
sudo cat ~/rustdesk/data/id_ed25519.pub

id_ed25519.pub 包含一个 base64 字符串。请将该字符串填入每个客户端的 Key 字段。id_ed25519 是私钥部分,绝不能离开服务器。公钥不属于机密,因为它本来就会复制到每个客户端的配置中。私钥属于机密:持有私钥的任何人都可以搭建一个客户端会信任的服务器。

现在就备份这两个文件,不要等到配置了二十个客户端之后再备份。

sudo tar czf ~/rustdesk-keys.tgz -C ~/rustdesk/data id_ed25519 id_ed25519.pub
sudo chmod 600 ~/rustdesk-keys.tgz

将该归档复制到此服务器之外。原因如下:这一步的重要性高于其他任何步骤。删除 ~/rustdesk/data,或者在新 VPS 上重建服务器时未复制该文件,hbbs 就会在下一次启动时生成新的密钥对。每个客户端仍保存旧公钥,因此 hbbs 会拒绝该客户端,客户端也会离线。运行 sudo cat ~/rustdesk/data/id_ed25519.pub,并将其输出与任意客户端的 Key 字段进行比较:两个字符串不再匹配,这个不匹配就是问题的全部原因。修复方法是在每台机器上手动编辑设置,包括那些原本依赖 RustDesk 访问的机器。

密钥不是会话密码。混淆这两者会导致用户漏掉其中一项。密钥决定服务器会与哪些客户端通信。受控机器上的永久密码或一次性代码决定谁可以在该机器上打开会话。两者都需要配置;其中一项配置正确,也无法弥补另一项过弱。

未认证中继为何存在问题

默认情况下,hbbr 不进行任何检查。RustDesk 配置文档对此有明确说明:密钥为空时,不包含匹配密钥的客户端也可以使用中继。默认使用空密钥,是为了避免新用户首次运行时遇到密钥不匹配错误。但代价是,任何找到您 TCP 21117 地址的人,都可以通过您的 VPS 转发会话流量,消耗您的流量额度,并使用您的 IP 地址。

command: hbbr -k _可以解决这个问题。_参数会让 hbbr 从工作目录加载密钥对;如果目录中没有密钥对,则生成一对。由于两个容器挂载的是同一个 ./data,因此使用的是 hbbs 的同一对密钥。无需手动复制文件,因此不会出现密钥不一致。

共享卷是最容易配置错误的部分。为 hbbr 单独创建目录后,它会生成不同的密钥对。此时 hbbs 和 hbbr 使用的密钥不一致,所有中继会话都会失败,而直连会话仍可正常工作。这个现象容易造成误判:RustDesk 能连接部分对端,无法连接其他对端,具体取决于打洞是否碰巧成功。一个 ls -l ~/rustdesk/data/ 中显示只有一对 id_ed25519,可以排除这种不匹配。但这不能证明中继会检查密钥,因为除非使用非空密钥启动 hbbr,否则 hbbr 会忽略这些文件。因此还要确认实际运行的命令:

ps -eo args | grep '[h]bbr'

输出中应包含 hbbr -k _。如果只显示 hbbr,则说明中继仍处于开放状态,无论目录中有什么内容。

将客户端指向您的服务器

在每台机器上打开 RustDesk,然后依次选择 Settings、Network、ID/Relay Server。

  • ID Server:填写您的主机名,例如 rustdesk.example.com。除非指定其他端口,否则客户端使用端口 21116。
  • Relay Server:当 hbbr 与 hbbs 运行在同一台主机上时留空。
  • API Server:留空。开源服务器不提供此服务。
  • Key:填写来自 id_ed25519.pub 的 base64 字符串,必须原样粘贴,末尾不能有空格。

此时主窗口应显示客户端已就绪。如果没有,并且客户端使用默认设置,通常原因是 UDP 21116 未能到达 hbbs,因为默认客户端会在该端口注册并发送心跳。不过,ID 显示离线本身不能证明这一点。将 disable-udp 选项设置为 Y 的客户端会改用 TCP 21116 注册,并且不会使用 UDP 21116。因此,对于该客户端,应先检查 TCP 21116。

限制端口,避免中继服务对外开放

由于容器使用主机网络,容器前面没有 Docker NAT 规则,因此 ufw 规则会按预期生效。

sudo ufw allow 22/tcp
sudo ufw allow 21115:21117/tcp
sudo ufw allow 21116/udp
sudo ufw enable
sudo ufw status numbered

只有运行浏览器客户端时才添加 21118:21119/tcp。启用 ufw 时保持第二个 SSH 会话处于打开状态。这样即使 SSH 规则配置错误,也不会将您锁在服务器之外。VPS 的 ufw 防火墙基础介绍默认策略和规则顺序。

现在说明一个容易踩坑的地方。如果改用 ports: 块发布端口,而许多单容器 RustDesk 示例都采用这种方式,Docker 会写入自己的 DNAT 规则。数据包会绕过 ufw 规则所在的链,直接到达容器。此时,对 21117 设置 ufw deny 不会生效。中继服务仍会对互联网开放,但 ufw status 会显示为其他状态。Docker 发布的端口会绕过 ufw介绍这些链的处理顺序。使用主机网络可以完全避免这个问题。如果确实要发布端口,请将其绑定到一个地址,例如反向代理后的 "127.0.0.1:21118:21118"。

按源地址限制访问,仅在客户端具有稳定地址时有效。

sudo ufw allow from 203.0.113.0/24 to any port 21115:21117 proto tcp
sudo ufw allow from 203.0.113.0/24 to any port 21116 proto udp

连接酒店网络的笔记本电脑没有稳定地址。因此,在这里,hbbr 上的密钥比防火墙发挥更实际的作用。

升级包含密钥的栈

密钥位于绑定挂载中,而不在容器内。因此,只要不改动 ./data,升级就是安全的。

  1. 先备份数据目录:sudo tar czf ~/rustdesk-data-backup.tgz -C ~/rustdesk data。
  2. 在 rustdesk-server releases 页面阅读新标签的发行说明。
  3. 编辑 compose.yml,将其中的两行 image: 都改为新标签。
  4. 运行 sudo docker compose pull,然后运行 sudo docker compose up -d。
  5. 运行 sudo cat ~/rustdesk/data/id_ed25519.pub,确认输出字符串与客户端当前持有的字符串一致。

第 5 步是关键检查,因为密钥变更时服务器不会显示明显错误,却会同时导致所有客户端失效。回滚时,将标签改回旧值,然后再次运行 up -d。这之所以可行,是因为你固定了标签:使用 latest 后,docker compose pull 将名称指向了新镜像,因此不再有标签指向旧镜像。

通常导致密钥丢失的不是 docker compose down,因为它不会改动绑定挂载。真正常见的原因是迁移到新的 VPS 时只复制了 compose.yml。请同时复制 ./data。

监控具有流量配额的套餐中的出站流量

hbbr 是此堆栈中唯一可能消耗流量配额的组件。RustDesk 的 FAQ 指出,在 1920x1080 屏幕上,一个中继连接的流量在 30 KB/s 到 3 MB/s 之间,普通办公操作约为 100 KB/s。这些是单个会话的公开数据,不是对您环境的测量结果。按每月 60 小时、每天 2 小时计算,结果如下。

图表VPS egress from one relayed RustDesk session, 60 hours per month
此图表背后的数据
[
  {
    "label": "Low end, 30 KB/s",
    "gb_per_month": 6.5
  },
  {
    "label": "Office work, 100 KB/s",
    "gb_per_month": 21.6
  },
  {
    "label": "High end, 3 MB/s",
    "gb_per_month": 648
  }
]

按普通办公操作的速率计算,一个会话每月约消耗 21.6 GB,任何套餐都不会明显受到影响。按公开范围的最高速率计算,同样 60 小时将消耗 648 GB;如果同时有两个会话以该速率运行,一个月内就会超过 1 TB 配额。按最低速率计算则为 6.5 GB。这里的 GB 按 1000 MB 计算,这也是流量配额通常采用的计量方式。

docker stats 无法为您拆分这些流量,因为使用 host 网络的容器共享主机的网络命名空间,因此其计数器就是主机的计数器。还有两个工具可以使用。vnstat 用于测量整台主机:

sudo apt install -y vnstat
sudo systemctl enable --now vnstat
vnstat -m

整台主机意味着整台主机:如果此 VPS 还运行着其他传输实际数据的服务,例如每晚从手机上传照片库的 自托管照片服务器,这些上传流量会与中继流量计入同一条月度统计。媒体服务器也是如此,只是方向相反。例如,将 Jellyfin 媒体库转换为可浏览的 90 年代视频商店的 Halcyon 会向观看者持续发送视频流,这些出站流量也会消耗中继所使用的配额。

nftables 计数器可以专门测量中继流量:

sudo nft add table inet relaymeter
sudo nft add chain inet relaymeter out '{ type filter hook output priority 0 ; }'
sudo nft add rule inet relaymeter out tcp sport 21117 counter
sudo nft list table inet relaymeter

该规则不做任何判定,因此只统计数据包和字节数,不改变放行结果;它位于独立的表中,不会影响 ufw。该配置不会持久保存:如果希望重启后恢复,请将相同的行写入 /etc/nftables.conf。只有在会话实际经过中继时,计数器才会增长。如果没有任何自有机器连接,但计数器仍持续增加,说明有人发现了您的中继;hbbr -k _ 正是用于防止这种情况的。您不会每天早上读取 nft list,因此应为它设置 cron 任务,将字节数与阈值进行比较,超过阈值时发送推送告警;这项工作应交给您自己的 ntfy 服务器。

hbbr 还提供可以调低的速率限制。SINGLE_BANDWIDTH 默认将每个中继连接限制为 128 Mb/s,TOTAL_BANDWIDTH 默认将所有连接合计限制为 1024 Mb/s。设置 SINGLE_BANDWIDTH=8 后,单个会话的速率会限制在约 1 MB/s。它限制的是传输速度,而不是每月总流量。因此,应将其视为防止单个会话占满链路的方法,而不是流量预算控制。

完全不需要中继时

对于个人环境,实际情况可能是您根本不需要这些组件。将两台机器加入网状 VPN,直接连接隧道地址即可。不需要 hbbs、hbbr,不产生中继出口流量,也不需要在 VPS 上维护待升级的容器。

在要控制的机器上,在 RustDesk 的安全设置中启用直接 IP 访问。端口字段默认为 21118。尝试连接前,确认该端口正在监听:

ss -tlnp | grep 21118

然后使用该对等端的 VPN 地址,而不是 ID 进行连接。RustDesk 的 FAQ 说明,此模式下的连接未加密,因此只能在隧道内使用,绝不能通过开放互联网连接。加密由隧道提供。

根据机器的所有者选择方案。如果您需要支持不属于您的机器,或者对方永远不会安装 VPN 客户端,则适合自托管 hbbs 和 hbbr,因为对方只需使用 ID 和密码。若所有机器都属于您,并且都能保存密钥,则适合使用支持直接 IP 访问的网状 VPN。WireGuard 与 Tailscale 的比较介绍了构建此网状网络的两种常见方式;在 Linux VPS 上运行远程桌面介绍了另一种情况,即您需要显示桌面的机器本身就是服务器。

FAQ

每个 RustDesk 会话都会经过我的中继服务器吗?

不会。hbbs 会先尝试让两个客户端直接连接,通过它们各自前面的 NAT 进行打洞。只有直接连接失败的会话才会回退到 hbbr,只有这些会话会消耗您的带宽。例外情况是 hbbs 上的 ALWAYS_USE_RELAY=Y,它会强制所有会话经过 hbbr,无论是否存在可用的直连路径。如果您在 Compose 文件中设置了该变量,每个会话的每个字节都会计入您的流量费用。

RustDesk 服务器密钥存储在哪里?丢失后怎么办?

hbbs 首次启动时,会在其工作目录中生成 id_ed25519 和 id_ed25519.pub。官方镜像中的该目录是 /root,因此使用上文所示的卷挂载后,这两个文件会出现在主机的 ./data 中。请将这两个文件备份到服务器之外。如果文件丢失,hbbs 会在下次启动时生成新的密钥对,所有仍持有旧公钥的客户端都会被拒绝连接。除了手动修改每个客户端的 Key 字段外,没有其他恢复方法。

自托管 RustDesk 服务器需要开放哪些端口?

需要开放 TCP 21115、21116 和 21117,以及 UDP 21116。hbbs 使用 21115 进行 NAT 类型测试,使用 21116 通过 UDP 进行 ID 注册和心跳检测,并通过 TCP 进行注册和打洞。hbbr 使用 21117 提供中继服务。TCP 21118 和 21119 是浏览器客户端使用的 WebSocket 端口;如果您不使用浏览器客户端,请保持关闭。TCP 21114 属于 Pro HTTP API 服务器,开源版本不需要该端口。

陌生人可以使用我自托管的 RustDesk 中继服务器吗?

可以,前提是您使用 hbbr 的默认配置运行。RustDesk 文档说明,空密钥允许未配置匹配密钥的客户端使用中继。因此,任何获知您的主机名和端口 21117 的人,都可以让网络流量经过您的服务器。请使用 -k _ 运行 hbbr,使其从共享的 ./data 卷加载 hbbs 使用的同一密钥对。之后,只有配置了您的公钥的客户端才能通过您的服务器进行中继。