让 RustDesk 走我自己的服务器

商业远控的连接都要过一次别人的服务器。自建 hbbs + hbbr 能把这个环节拿回来,代价只是一条 docker compose。国内拉镜像和 latest 标签,是最容易卡住的两处。

TeamViewer、ToDesk、向日葵这类远控,连接都要过一次厂商的服务器。自建 RustDesk 服务端的意义在于把这个环节拿回来,代价是一条 `docker compose`。

它由两个组件组成,加起来不到 200 MB 内存。

以上是 AI 帮我写的,实际上,我用 RustDesk 的原因是: ChatGPT 力荐,我有一个ECS。

先决条件

需要 说明
一台有公网 IP 的服务器 客户端要能直接连上它,所以不能是纯内网机
Docker + Docker Compose 部署方式,两个容器
能改云服务商的安全组 端口要在安全组和服务器本机防火墙两处都开
RustDesk 客户端 Windows / macOS / Linux / Android / iOS 都有

三个自查命令:

```bash
docker --version              # 有 Docker 就行
docker compose version        # 注意是 compose 子命令,不是 docker-compose
```

这套做法在什么环境上验证过

```plaintext
部署时间:2026-07-20 | 镜像 rustdesk/rustdesk-server:1.1.15
服务端:国内云服务器 | Docker Compose | network_mode: host
客户端:Linux (CachyOS) | 自定义 ID 服务器 + 中继 + 密钥
```
项 是否通用 说明
hbbs / hbbr 的职责与端口 ✅ 通用 RustDesk 自身设计
compose 文件结构 ✅ 通用 与云厂商无关
国内拉镜像 ⚠️ 只对国内服务器 有外网或已配加速器的可以跳过那一节
防火墙命令 ⚠️ 因发行版而异 `ufw` 还是 `firewalld`,先看自己那台
镜像版本号 ❌ 会过时 写这篇时是 1.1.15,装之前去看一眼 Releases

它由两个组件组成

组件 容器 作用
hbbs `rustdesk-hbbs` 设备注册、NAT 类型测试、P2P 连接协调
hbbr `rustdesk-hbbr` P2P 直连失败时,负责中继转发
```plaintext
A 想连 B
 └─ 先问 hbbs:B 在哪?能直连吗?
     ├─ 能 ──→ 数据走 P2P,不经服务器
     └─ 不能 ──→ 走 hbbr 中继
                  (对称 NAT 之类)
```

大多数时候流量是不经过服务器的,服务器只负责牵线。这也是它便宜的原因: 一台最低配的机器就够了,只有在中继时才吃带宽。

端口

端口 协议 归属 用途
21115 TCP hbbs NAT 类型测试
21116 TCP hbbs ID 注册与心跳
21116 UDP hbbs 打洞 / NAT 穿透
21117 TCP hbbr 中继服务
21118 TCP hbbs WebSocket,可选

21115 TCP、21116 TCP + UDP、21117 TCP 四个必须开。

21116 那个 UDP 最容易漏。只开 TCP 的话,客户端能注册上线,但打洞会失败, 于是所有流量都被挤到中继上,速度明显不对。

第一步:写 compose 文件

```bash
mkdir -p /opt/rustdesk-server && cd /opt/rustdesk-server
```

写入 `compose.yml`:

```yaml
services:
  hbbs:
    container_name: rustdesk-hbbs
    image: rustdesk/rustdesk-server:1.1.15      # 必须写具体版本号
    command: hbbs -r <你的服务器IP>:21117 -k _
    volumes:
      - ./data:/root
    network_mode: host
    restart: unless-stopped
    depends_on:
      - hbbr

  hbbr:
    container_name: rustdesk-hbbr
    image: rustdesk/rustdesk-server:1.1.15      # 同上
    command: hbbr -k _
    volumes:
      - ./data:/root
    network_mode: host
    restart: unless-stopped
```

参数逐个说:

参数 含义
`-r <IP>:21117` 告诉客户端中继服务器在哪。填公网 IP
`-k _` 强制密钥认证。首次启动自动生成密钥对
`network_mode: host` 直接用宿主机网络栈,端口不用再做映射
`./data:/root` 持久化密钥和数据库。这个目录丢了,所有客户端都要重配

⚠️ 坑一:绝不能用 `latest`

注意,这篇文章的更新时间虽然是`2026-9-20`,但实际上笔者在这一天并没有真正意义上去测试,所以这一条的时间线为`2026-07-20`

这是这篇里最值得记的一条。用 `latest` 拉到旧版本镜像后,容器起不来,日志里是:

```plaintext
ERROR: Registered email required (-m option).
Please pay and register on https://rustdesk.com/server
```

看着像「要付费注册」,其实是国内某些镜像源缓存了几年前的旧版本, 而旧版本要求商业授权。写死版本号就能绕开。

装之前先去 Releases 看一眼当前版本,把 `1.1.15` 换成它。

⚠️ 坑二:国内服务器拉不动镜像

如果服务器在国内且没有代理,`docker compose pull` 会卡在 Docker Hub 上。两个办法:

办法一:直接用代理前缀拉,再打回原标签

```bash
docker pull docker.1ms.run/rustdesk/rustdesk-server:1.1.15
docker tag docker.1ms.run/rustdesk/rustdesk-server:1.1.15 \
           rustdesk/rustdesk-server:1.1.15
```

改完标签,compose 文件里的 `image` 不用动。

办法二:给 Docker 配加速器,一劳永逸

```bash
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<'EOF'
{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker.xuanyuan.me",
    "https://docker.m.daocloud.io"
  ]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
```

配多个源是为了容灾,某一个挂了还有别的。之后 `docker compose pull` 就能直接拉。

这些镜像代理的可用性会变,用之前先自己试一下。写这篇时三个都还能用。

第二步:启动并拿到密钥

```bash
cd /opt/rustdesk-server
docker compose up -d

# 拿公钥
cat ./data/id_ed25519.pub
```

密钥文件在 `./data/` 下,是首次启动时自动生成的。客户端要填的就是 `id_ed25519.pub` 里的那一串。

第三步:开端口

两处都要开,只开一处等于没开:

服务器本机防火墙

```bash
# ufw(Debian / Ubuntu)
sudo ufw allow 21115:21117/tcp
sudo ufw allow 21116/udp
sudo ufw reload
```
```bash
# firewalld(RHEL / CentOS / Fedora)
sudo firewall-cmd --permanent --add-port=21115-21117/tcp
sudo firewall-cmd --permanent --add-port=21116/udp
sudo firewall-cmd --reload
```

云服务商的安全组,在控制台里放行同样这几个端口。

第四步:配客户端

在 RustDesk 客户端里进 设置 → 网络,填三样:

字段 填什么
ID 服务器 你的服务器 IP
中继服务器 同一个 IP(端口不用写,默认 21117)
密钥 `id_ed25519.pub` 的内容

点应用,状态变绿显示「就绪」就是通了。

手机上也要各填一遍。 这几项不会跟着账号走,换设备就得重填。

怎么验证

① 服务端在不在

```bash
docker compose ps            # 两个容器都应该是 Up
docker logs rustdesk-hbbs    # 没有报错
```

② 端口对外通不通

在另一台机器上跑,不要在服务器本机跑(本机跑测不出安全组):

```bash
for p in 21115 21116 21117; do
  timeout 3 bash -c "echo > /dev/tcp/<你的服务器IP>/$p" 2>/dev/null \
    && echo "$p 开着" || echo "$p 不通"
done
```

三个都应该是「开着」。

③ 客户端连上后看状态

RustDesk 主界面左下角,状态是「就绪」就说明 ID 服务器通了。

④ 真正连一次

从手机连一次电脑,看画面能不能出来。这一步才作数 —— 前面三项目前全绿、 最后连不上,通常是中继或者 UDP 打洞的问题。

排错

现象 可能原因 下一步
容器起不来,日志提「paid and register」 用了 `latest`,拉到旧版本 改成具体版本号重新拉
`docker compose pull` 卡住 国内直连 Docker Hub 用代理前缀,或配加速器
客户端显示「未就绪」 21116 没通 安全组和本机防火墙都查一遍
能连上但很卡 P2P 没打通,全走中继 检查 21116 UDP 开了没有
换服务器后客户端连不上 密钥变了 重新 `cat ./data/id_ed25519.pub` 填进去
重装服务器后全部客户端失效 `./data/` 没了,密钥重新生成 把 `./data/` 备份出来

参考