本文面向在 Ubuntu 上自托管 RustDesk 开源服务器(hbbs/hbbr)。适用于本地机房或云服务器(如阿里云、腾讯云、华为云等)。
- 官方参考文档(强烈建议同时阅读):rustdesk.com/docs/zh-cn/...
- 适用系统:Ubuntu 20.04/22.04/24.04(其他 Debian 系亦可参考)
0. 准备工作
- 一台可公网访问的 Ubuntu 服务器(具备
sudo权限)。 - 确保可以通过
ssh登录,执行基础维护命令。 - 如果使用云厂商,先在云控制台开放端口。
1. 开放端口
云厂商安全组是"第一道防火墙",仅配置系统 ufw 不够,必须在云控制台放行端口。以阿里云为例:
- 登录 阿里云 ECS 控制台 → 找到你的服务器 → 左侧「安全组」→ 配置规则。
- 新增「入方向」规则,放行 RustDesk 所需端口:
| 协议 | 端口范围 | 授权对象 | 说明 |
|---|---|---|---|
| TCP | 21114-21119 | 0.0.0.0/0 | RustDesk 核心 TCP 端口 |
| UDP | 21116 | 0.0.0.0/0 | RustDesk NAT 打洞 UDP 端口 |
提示:0.0.0.0/0 表示允许所有 IP(适合测试)。生产环境建议缩小到你的办公网段或指定客户端 IP。
保存规则后,通常需等待 1--2 分钟生效(安全组有延迟)。
同时,在服务器系统内使用 ufw 放行端口(建议):
shell
sudo apt update
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 21114:21119/tcp
sudo ufw allow 21116/udp
sudo ufw enable
sudo ufw status numbered
2. 安装 RustDesk(方式一:一键脚本)
如果你希望快速完成部署,可使用一键脚本:
shell
wget https://ghfast.top/https://raw.githubusercontent.com/techahold/rustdeskinstall/master/install.sh -O install.sh
chmod +x install.sh
sudo ./install.sh
脚本会自动下载安装并以 systemd 服务运行 hbbs(ID 服务)与 hbbr(中继服务)。
- 安装过程中会出现两处交互确认,按提示选择即可(见下图)。


- 首次启动时,服务会在日志中输出一个
Key(密钥),请复制并保存(见下图)。

查看服务状态与日志:
shell
systemctl status hbbs
systemctl status hbbr
journalctl -u hbbs -f
journalctl -u hbbr -f
检查端口是否监听:
shell
ss -tunlp | grep -E '2111[4-9]|21116'
3. 安装 RustDesk(方式二:官方 deb 包,二选一)
也可在 GitHub Releases 下载官方 .deb 包并安装(适合熟悉 Debian 包管理的用户):
-
前往 RustDesk 服务器开源版发布页,下载适合架构的
*.deb。 -
上传到服务器并安装:
shellsudo dpkg -i rustdesk-server*.deb sudo apt -f install -
验证服务:
shellsystemctl status hbbs systemctl status hbbr
两种安装方式任选其一即可,不必重复安装。
4. 客户端配置(Windows/macOS/Linux)
在 RustDesk 客户端中填写自托管信息(见下图):
- 打开 RustDesk → 进入「设置」→ 找到与「自托管 / 服务器」相关的配置。
- 填写以下内容:
ID 服务器:你的服务器域名或公网 IP(端口默认 21114)。中继服务器:你的服务器域名或公网 IP(默认端口 21117,可在客户端处填写为IP:21117)。Key:从服务器日志中复制的密钥。

保存后,返回主界面,两个客户端互相输入对方 ID 进行连接测试。若能直连,会显示直连;若 NAT 复杂,则会自动走中继。
5. 远程连通性验证(可选但推荐)
从你的本地电脑测试云端端口是否可达:
shell
nc -zv <你的服务器IP或域名> 21114
nc -zv <你的服务器IP或域名> 21117
如显示 succeeded 或 open,表示端口连通性正常。
6. 常见问题与排查
- 无法获取 ID:
- 检查
hbbs服务是否运行:systemctl status hbbs - 检查云安全组与
ufw是否已放行 21114--21119/TCP 与 21116/UDP。
- 检查
- 连接总是走中继:
- 很可能是客户端之间 NAT 穿透失败。确保 21116/UDP 放行;或接受通过中继连接。
- 提示 Key 不匹配:
- 确保客户端填写的
Key与服务器当前输出的一致;变更服务器后需更新客户端配置。
- 确保客户端填写的
- 服务名不确定:
- 可执行
systemctl list-units | grep hbbs与grep hbbr查找具体服务名称。
- 可执行
7. 运维与升级(简要)
-
重启服务:
shellsudo systemctl restart hbbs sudo systemctl restart hbbr -
日志查看:
shelljournalctl -u hbbs --no-pager -n 200 journalctl -u hbbr --no-pager -n 200 -
安全建议:
- 生产环境将安全组授权对象从
0.0.0.0/0收紧到可信网段。 - 定期更新系统并重启服务,减少已知漏洞风险。
- 生产环境将安全组授权对象从
8. 一键更换默认端口脚本(含安全收敛)
为便于快速落地,提供一键脚本:
- 让
hbbs/hbbr在自定义端口监听(需要提供中继主机与可选密钥文件)。
端口范围与建议:
- 可修改范围:
1024--65535(避免使用0--1023的保留端口)。 - 推荐选择高位端口(如
20000--40000),并提前检查占用:ss -tunlp | grep <PORT>。
将脚本文件 rustdesk-port-switch.sh 上传到服务器(例如 /usr/local/sbin/),并赋予执行权限:
shell
#!/usr/bin/env bash
set -euo pipefail
# 常量: 默认端口
DEFAULT_NAT=21116
DEFAULT_RELAY=21117
DEFAULT_HBBS_WS=21118
DEFAULT_HBBR_WS=21119
BASE=""
NAT_PORT=""
RELAY_PORT=""
HBBS_WS=""
HBBR_WS=""
RELAY_HOST=""
# 函数: 打印用法说明与端口范围
usage() {
cat <<'USAGE'
用法: sudo bash rustdesk-port-switch.sh
说明: 纯交互式,直接修改 hbbs/hbbr 监听端口(override 模式)
端口范围: 1024--65535,建议 20000--40000,避免 0--1023 保留端口
USAGE
}
# 函数: 检查是否 root
require_root() {
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
echo "需要以 root 运行" >&2
exit 1
fi
}
# 函数: 验证端口范围与合法性
validate_port() {
local p="$1"
[[ "$p" =~ ^[0-9]+$ ]] || { echo "端口必须为数字: $p" >&2; exit 1; }
if [ "$p" -lt 1024 ] || [ "$p" -gt 65535 ]; then
echo "端口超出允许范围(1024--65535): $p" >&2
exit 1
fi
}
# 函数: 计算最终端口组合
compute_ports() {
if [ -n "$BASE" ]; then
NAT_PORT="$BASE"
RELAY_PORT=$((BASE+2))
# WebSocket 端口仅在显式指定时设置
fi
: "${NAT_PORT:=${NAT_PORT:-}}"
: "${RELAY_PORT:=${RELAY_PORT:-}}"
[ -n "$NAT_PORT" ] || NAT_PORT="$DEFAULT_NAT"
[ -n "$RELAY_PORT" ] || RELAY_PORT="$DEFAULT_RELAY"
validate_port "$NAT_PORT"; validate_port "$RELAY_PORT"
[ -n "$HBBS_WS" ] && validate_port "$HBBS_WS"
[ -n "$HBBR_WS" ] && validate_port "$HBBR_WS"
}
# 函数: 交互式询问模式/端口/安全选项
is_port_used() {
local p="$1"
ss -H -l -n | awk '{print $5}' | awk -F: '{print $NF}' | grep -qx "$p"
}
suggest_base_port() {
local tries=0 max=30 b
while [ $tries -lt $max ]; do
b=$((20000 + RANDOM % 20001))
[ $((b+4)) -le 65535 ] || { tries=$((tries+1)); continue; }
if is_port_used "$b" || is_port_used $((b+2)) || is_port_used $((b+3)) || is_port_used $((b+4)); then
tries=$((tries+1)); continue
fi
BASE_SUG="$b"
HBBS_WS_SUG=$((b+3))
HBBR_WS_SUG=$((b+4))
return 0
done
BASE_SUG=30022
HBBS_WS_SUG=30025
HBBR_WS_SUG=30026
}
interactive_prompt() {
suggest_base_port
echo "推荐基准端口: ${BASE_SUG} (hbbs(NAT)=${BASE_SUG}, hbbr(Relay)=$((BASE_SUG+2)); WS: hbbs=${HBBS_WS_SUG}, hbbr=${HBBR_WS_SUG})"
echo "可输入基准端口(如 30022), 将自动设置: hbbs(NAT)=base, hbbr(Relay)=base+2"
read -r -p "基准端口(回车使用推荐 ${BASE_SUG}): " BASE
if [ -z "$BASE" ]; then BASE="$BASE_SUG"; fi
validate_port "$BASE"
NAT_PORT="$BASE"; RELAY_PORT=$((BASE+2))
# 可选 WebSocket 端口
read -r -p "是否设置 WebSocket 端口(推荐 hbbs=${HBBS_WS_SUG} hbbr=${HBBR_WS_SUG},官方默认 21118/21119)[y/N]: " ws_sel
case "${ws_sel,,}" in
y|yes)
echo "推荐 WebSocket 端口: hbbs=${HBBS_WS_SUG} hbbr=${HBBR_WS_SUG}"
read -r -p "hbbs WebSocket 端口(回车使用推荐 ${HBBS_WS_SUG}): " HBBS_WS
[ -z "$HBBS_WS" ] && HBBS_WS="$HBBS_WS_SUG"
validate_port "$HBBS_WS"
read -r -p "hbbr WebSocket 端口(回车使用推荐 ${HBBR_WS_SUG}): " HBBR_WS
[ -z "$HBBR_WS" ] && HBBR_WS="$HBBR_WS_SUG"
validate_port "$HBBR_WS"
;;
*) : ;;
esac
read -r -p "中继主机(域名或IP): " RELAY_HOST
if [ -z "$RELAY_HOST" ]; then
echo "需要提供中继主机"; exit 1
fi
echo "\n配置摘要:";
echo "hbbs(NAT) 端口: ${NAT_PORT} hbbr(Relay) 端口: ${RELAY_PORT}"
[ -n "$HBBS_WS" ] && echo "hbbs WS: ${HBBS_WS}"
[ -n "$HBBR_WS" ] && echo "hbbr WS: ${HBBR_WS}"
echo "中继主机: ${RELAY_HOST}"
read -r -p "继续执行? [y/N]: " go
case "${go,,}" in y|yes) : ;; *) echo "已取消"; exit 1;; esac
}
# (移除 NAT 重定向逻辑)
# 函数: UFW 开放新端口
ensure_ufw_rules() {
if ! command -v ufw >/dev/null 2>&1; then
apt-get update -y && apt-get install -y ufw
fi
ufw allow "${NAT_PORT}/udp" || true
ufw allow "${NAT_PORT}/tcp" || true
ufw allow "${RELAY_PORT}/tcp" || true
[ -n "$HBBS_WS" ] && ufw allow "${HBBS_WS}/tcp" || true
[ -n "$HBBR_WS" ] && ufw allow "${HBBR_WS}/tcp" || true
ufw status | cat
}
# (移除持久化 iptables 逻辑)
# 函数: 写入 systemd override
write_override() {
local hbbs_path hbbr_path unit path_guess
# 优先使用用户指定路径
hbbs_path="${HBBS_PATH:-}"
hbbr_path="${HBBR_PATH:-}"
# 通过 PATH 查找
[ -z "$hbbs_path" ] && hbbs_path="$(command -v hbbs || true)"
[ -z "$hbbr_path" ] && hbbr_path="$(command -v hbbr || true)"
# 通过 systemd 单元解析 ExecStart
if [ -z "$hbbs_path" ]; then
unit="$(systemctl cat hbbs 2>/dev/null || true)"
path_guess="$(echo "$unit" | awk -F= '/^ExecStart=/{print $2}' | awk '{print $1}' | tail -n1)"
[ -n "$path_guess" ] && hbbs_path="$path_guess"
fi
if [ -z "$hbbr_path" ]; then
unit="$(systemctl cat hbbr 2>/dev/null || true)"
path_guess="$(echo "$unit" | awk -F= '/^ExecStart=/{print $2}' | awk '{print $1}' | tail -n1)"
[ -n "$path_guess" ] && hbbr_path="$path_guess"
fi
# 常见路径回退
[ -z "$hbbs_path" ] && for p in /usr/local/bin/hbbs /usr/bin/hbbs /opt/rustdesk/hbbs /opt/rustdesk-server/hbbs; do [ -x "$p" ] && hbbs_path="$p" && break; done
[ -z "$hbbr_path" ] && for p in /usr/local/bin/hbbr /usr/bin/hbbr /opt/rustdesk/hbbr /opt/rustdesk-server/hbbr; do [ -x "$p" ] && hbbr_path="$p" && break; done
[ -n "$hbbs_path" ] && [ -x "$hbbs_path" ] || { echo "未找到可执行的 hbbs, 请确认安装在 /opt/rustdesk/hbbs 或 /usr/local/bin/hbbs"; exit 1; }
[ -n "$hbbr_path" ] && [ -x "$hbbr_path" ] || { echo "未找到可执行的 hbbr, 请确认安装在 /opt/rustdesk/hbbr 或 /usr/local/bin/hbbr"; exit 1; }
# 写入 systemd 配置:若主单元存在则写 override;否则创建新的单元文件
if systemctl cat hbbs >/dev/null 2>&1; then
mkdir -p /etc/systemd/system/hbbs.service.d
cat >/etc/systemd/system/hbbs.service.d/override.conf <<EOF
[Service]
ExecStart=
ExecStart=${hbbs_path} -p ${NAT_PORT} -r ${RELAY_HOST}:${RELAY_PORT}
EOF
else
cat >/etc/systemd/system/hbbs.service <<EOF
[Unit]
Description=RustDesk HBBS (ID server)
After=network.target
[Service]
Type=simple
ExecStart=${hbbs_path} -p ${NAT_PORT} -r ${RELAY_HOST}:${RELAY_PORT}
Restart=always
RestartSec=2s
User=root
[Install]
WantedBy=multi-user.target
EOF
fi
if systemctl cat hbbr >/dev/null 2>&1; then
mkdir -p /etc/systemd/system/hbbr.service.d
cat >/etc/systemd/system/hbbr.service.d/override.conf <<EOF
[Service]
ExecStart=
ExecStart=${hbbr_path} -p ${RELAY_PORT}
EOF
else
cat >/etc/systemd/system/hbbr.service <<EOF
[Unit]
Description=RustDesk HBBR (Relay server)
After=network.target
[Service]
Type=simple
ExecStart=${hbbr_path} -p ${RELAY_PORT}
Restart=always
RestartSec=2s
User=root
[Install]
WantedBy=multi-user.target
EOF
fi
}
# 函数: 重载并重启服务
restart_services() {
systemctl daemon-reload
systemctl enable hbbs >/dev/null 2>&1 || true
systemctl enable hbbr >/dev/null 2>&1 || true
systemctl restart hbbs || true
systemctl restart hbbr || true
systemctl --no-pager -q status hbbs hbbr || true
}
# 函数: 提取并保存最新 Key 到脚本同级目录,并打印
save_and_print_key() {
local key line script_dir key_file
# 从 hbbs 日志提取最新 Key
line=$(journalctl -u hbbs -n 200 --no-pager 2>/dev/null | grep -E "Key:" | tail -n1 || true)
if [ -z "$line" ]; then
line=$(journalctl -u hbbr -n 200 --no-pager 2>/dev/null | grep -E "Key:" | tail -n1 || true)
fi
key=$(echo "$line" | sed -E 's/^.*Key:\s*([A-Za-z0-9+\/=]+).*$/\1/' )
script_dir=$(cd -- "$(dirname -- "$0")" && pwd)
key_file="$script_dir/rustdesk.key"
if [ -n "$key" ]; then
printf "%s\n" "$key" > "$key_file"
chmod 600 "$key_file" 2>/dev/null || true
echo "- ID 服务器 : ${RELAY_HOST}:${NAT_PORT}"
echo "- 中继服务器 : ${RELAY_HOST}:${RELAY_PORT}"
echo "- Key: $key"
echo "已保存到: $key_file"
else
echo "- ID 服务器 : ${RELAY_HOST}:${NAT_PORT}"
echo "- 中继服务器 : ${RELAY_HOST}:${RELAY_PORT}"
echo "- Key: (未解析)"
echo "未从日志解析到 Key。可稍后运行: journalctl -u hbbs -n 200 | grep -i Key"
fi
}
require_root
interactive_prompt
if [ -z "$RELAY_HOST" ]; then
echo "需要提供中继主机" >&2
exit 1
fi
write_override
restart_services
ensure_ufw_rules
save_and_print_key
shell
sudo mv rustdesk-port-switch.sh /usr/local/sbin/
sudo chmod +x /usr/local/sbin/rustdesk-port-switch.sh
交互式运行(无需参数):
shell
sudo bash /usr/local/sbin/rustdesk-port-switch.sh
- 将依次询问基准端口或分别输入 hbbs/hbbr 端口、是否设置 WebSocket 端口,以及中继主机与密钥文件,确认后自动执行。
- 已内置自动探测
hbbs/hbbr路径(包含/opt/rustdesk/hbbs与/opt/rustdesk/hbbr等常见位置),无需手动输入路径。 - 每次运行脚本都会随机推荐一组高位端口(避开已占用),包含
hbbs(NAT)、hbbr(Relay)以及可选的 WebSocket 端口;直接回车可使用推荐值。 - 脚本会在执行完成后自动从日志解析最新
Key,并保存为脚本同级目录的rustdesk.key(覆盖旧文件),同时在终端打印当前Key。
执行完成后终端将输出如下摘要,便于在客户端填写:
- ID 服务器 :
<你的域名或IP>:<hbbs端口> - 中继服务器 :
<你的域名或IP>:<hbbr端口> - Key:
<服务器当前Key> - 已保存到:
<脚本同级目录>/rustdesk.key
记得去阿里云等安全组开放重新设置的端口
验证:3001[5-9]记得换成<新设置的端口>
shell
ss -tunlp | grep -E '3001[5-9]'
nc -zv <域名或IP> <新设置的端口>
nc -zv <域名或IP> <新设置的端口>
9. 端口说明(了解即可)
RustDesk 服务器默认使用 21114--21119/TCP 与 21116/UDP:
21116/UDP:NAT 打洞。21117/TCP:中继通道。- 其余端口用于 ID 注册、握手与控制信令等(保留默认即可)。