RustDesk is an open-source remote desktop client that works like TeamViewer or AnyDesk - except the server side is open source, you can host it yourself, and there is no per-seat pricing. The official public relay is fine for casual use, but for anything serious (support work, family helpdesk, or a small team) running your own server gives you faster connections, no session limits, and full control over who can connect.
This tutorial walks through a production-ready install on a VPS: Docker Compose, the open-source hbbs (rendezvous) and hbbr (relay) services, firewall rules, and the client-side configuration to point every device at your own server.
hbbs (signaling) and hbbr (relay) with a single compose fileTotal time: about 10 minutes.
Two small services do the work:
21116 plus TCP 21115, 21116, 21118.21117 and 21119.When you launch a session, the client first asks hbbs where the peer is. If both peers can hole-punch, they connect directly and the relay is never used. If they can't (typical with strict carrier NATs), traffic falls back to hbbr.
For a low-latency relay you want a VPS close to where your clients live. Anything between the two endpoints works, but a server in your own region usually beats the public free relay by a wide margin.
rd.example.com pointed at the VPS (nicer than typing IPs into clients)A 1-vCPU box is plenty. The relay is mostly bandwidth-bound, not CPU-bound.
If you have a domain handy, this makes life easier when typing the server address into clients.
In your DNS provider, add an A record:
rd.example.com → YOUR_VPS_IPV4
Verify:
dig +short rd.example.com
The IP should match. Skip this step if you'd rather just use the raw IP.
On a fresh Ubuntu server:
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Confirm it's working:
docker --version
docker compose version
RustDesk needs five ports open. Some are TCP, one is both TCP and UDP.
sudo ufw allow 22/tcp
sudo ufw allow 21115/tcp
sudo ufw allow 21116/tcp
sudo ufw allow 21116/udp
sudo ufw allow 21117/tcp
sudo ufw allow 21118/tcp
sudo ufw allow 21119/tcp
sudo ufw enable
If you use a provider firewall or security group, mirror the same rules there. Missing the UDP rule on 21116 is the single most common cause of "client connects but never relays" later.
sudo mkdir -p /opt/rustdesk
cd /opt/rustdesk
sudo mkdir -p data
The data directory holds the public/private key pair that identifies your server. Back this up.
Create /opt/rustdesk/docker-compose.yml:
services:
hbbs:
image: rustdesk/rustdesk-server:latest
container_name: hbbs
command: hbbs
network_mode: host
volumes:
- ./data:/root
restart: unless-stopped
depends_on:
- hbbr
hbbr:
image: rustdesk/rustdesk-server:latest
container_name: hbbr
command: hbbr
network_mode: host
volumes:
- ./data:/root
restart: unless-stopped
A few notes:
network_mode: host is the simplest setup. Without it you have to publish each port and the relay's NAT detection gets confused.data volume so they read the same key pair.latest tag pins to the OSS server. Switch to a specific version tag if you want reproducible upgrades.cd /opt/rustdesk
sudo docker compose up -d
sudo docker compose logs -f
On first launch, hbbs generates a key pair under ./data. You should see something like:
hbbs | Key: <BASE64_PUBLIC_KEY>
hbbs | Listening on tcp/udp :21116, tcp :21115/21118
hbbr | Listening on tcp :21117/21119
Copy the Key: value. You'll paste it into every client in the next step. You can also read it later from:
sudo cat /opt/rustdesk/data/id_ed25519.pub
In every RustDesk app (desktop, mobile), open Settings and then Network (or ID/Relay Server on older versions) and fill in:
rd.example.com (or your VPS IP)rd.example.comid_ed25519.pub value from Step 6Save. The status bar at the bottom of the main RustDesk window should switch from Ready over the public relay to Ready with a green badge showing your server name. The connection ID will also change.
If you're rolling this out to non-technical users, the easier path is a custom client build with the server pre-configured. RustDesk's build customization docs walk through it. For up to a handful of clients, manual config is fine.
On two devices that both point at your server:
Control Remote Desktop box and hit ConnectWatch the relay logs while you connect:
sudo docker compose logs hbbr --tail 50
If you see relay handshake from <IP> traffic is flowing through your relay. If you see no relay activity, the peers connected directly - that's actually the better case, since direct is faster than relayed.
By default, anyone who knows your server address and public key can register an ID with it. For a personal or small-team relay, that's usually fine - the key value is what stops random strangers from joining.
If you want stricter access, two common options:
Restrict by IP. Only let your office and home IPs reach the RustDesk ports:
sudo ufw delete allow 21115/tcp
sudo ufw delete allow 21116/tcp
sudo ufw delete allow 21116/udp
sudo ufw delete allow 21117/tcp
sudo ufw delete allow 21118/tcp
sudo ufw delete allow 21119/tcp
for port in 21115/tcp 21116/tcp 21116/udp 21117/tcp 21118/tcp 21119/tcp; do
sudo ufw allow from 203.0.113.10 to any port "${port%/*}" proto "${port#*/}"
done
Put it behind a VPN. Install Tailscale or WireGuard on the VPS and on each client device, and only allow the RustDesk ports from the VPN range. This is the cleanest setup for a personal helpdesk relay - see our Tailscale guide for the full walkthrough.
The keys in /opt/rustdesk/data are the only piece of state you cannot regenerate without touching every client.
sudo tar -czf /var/backups/rustdesk-keys-$(date +%F).tar.gz \
-C /opt/rustdesk data
Drop this on a cron job and copy it off-box weekly:
echo "30 3 * * 0 root tar -czf /var/backups/rustdesk-keys-\$(date +\\%F).tar.gz -C /opt/rustdesk data" | \
sudo tee /etc/cron.d/rustdesk-backup
If you ever lose the keys, every client has to be reconfigured with the new public key.
cd /opt/rustdesk
sudo docker compose pull
sudo docker compose up -d
The OSS server is small and rarely breaks across versions. Take a fresh key backup before bumping major versions just in case.
Clients show Ready but every connection times out. UDP port 21116 is blocked. RustDesk uses it for hole punching. Check both ufw status and your provider firewall.
Connections work but feel slow. Either the relay is doing all the work (NAT punching failed) and your VPS is far from the clients, or your VPS uplink is saturated. Pick a region closer to your users, or upgrade to a server with more bandwidth.
Key mismatch error in the client. The Key field is wrong or has trailing whitespace. Re-copy the value from id_ed25519.pub on the server.
hbbs keeps restarting. Almost always a port already in use. Check with sudo ss -tulpn | grep 2111 and stop any conflicting service.
You restored a backup and clients can't connect. The public key changed. Update the Key field on every client, or restore the original key files from a previous backup.
Relay Server field.Self-hosted RustDesk gives you the convenience of TeamViewer with none of the licensing pain. Once it's running, the only ongoing cost is the VPS itself.
Need a VPS to put your RustDesk relay on? Our Linux plans include fast IPv4, generous bandwidth, and a global network of locations. See the options.