RustDesk is an open-source remote desktop application, similar to TeamViewer or AnyDesk. By default its clients use public servers run by the RustDesk project; hosting your own server keeps device registration and relayed sessions on infrastructure you control. The server has two components: hbbs, the ID and rendezvous server, and hbbr, the relay used when two clients cannot connect directly. In this tutorial you will run both on Ubuntu 24.04 with Docker Compose and configure clients to use them.
Prerequisites
To follow this tutorial, you will need:
- A server running Ubuntu 24.04 LTS with a public IP address, for example a CubePath VPS with 1 vCPU and 1 GB of RAM, and a non-root user with
sudoprivileges. - Optionally, a domain name such as
rustdesk.your_domainwith a DNS A record pointing toyour_server_ip. Clients can also use the IP directly. - The RustDesk client installed on at least two machines, downloaded from the official GitHub releases page or
rustdesk.com.
Step 1 - Installing Docker Engine
Install Docker Engine and the Compose plugin from Docker's official repository. Add the signing key:
sudo apt update
sudo apt install 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
Add the repository:
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
Install the packages and check that Compose is available:
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker compose version
Docker Compose version v2.x.x
Step 2 - Opening the firewall ports
RustDesk uses the following ports:
| Port | Protocol | Component | Purpose |
|---|---|---|---|
| 21115 | TCP | hbbs | NAT type test |
| 21116 | TCP and UDP | hbbs | ID registration, heartbeat and hole punching |
| 21117 | TCP | hbbr | Relay |
| 21118, 21119 | TCP | hbbs, hbbr | Web client only (optional) |
Allow the required ones with UFW, making sure SSH stays open:
sudo ufw allow OpenSSH
sudo ufw allow 21115:21117/tcp
sudo ufw allow 21116/udp
sudo ufw enable
sudo ufw status
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
21115:21117/tcp ALLOW Anywhere
21116/udp ALLOW Anywhere
The UDP rule for 21116 matters: without it clients fail to register or cannot establish direct connections.
Step 3 - Starting hbbs and hbbr with Docker Compose
Create a directory for the server and its data:
sudo mkdir -p /opt/rustdesk
Create the Compose file:
sudo nano /opt/rustdesk/compose.yaml
services:
hbbs:
container_name: hbbs
image: rustdesk/rustdesk-server:latest
command: hbbs
volumes:
- ./data:/root
network_mode: host
depends_on:
- hbbr
restart: unless-stopped
hbbr:
container_name: hbbr
image: rustdesk/rustdesk-server:latest
command: hbbr
volumes:
- ./data:/root
network_mode: host
restart: unless-stopped
Both containers use host networking, so they bind the ports from Step 2 directly on the server and a ports: section is not needed. The shared ./data volume stores the server key pair and the device database.
Start the stack:
cd /opt/rustdesk
sudo docker compose up -d
Check that both containers are running and listening:
sudo docker compose ps
sudo ss -tulpn | grep -E ':2111[5-9]'
You should see hbbs listening on 21115, 21116 (TCP and UDP) and 21118, and hbbr on 21117 and 21119.
Step 4 - Retrieving the public key
On first start, hbbs generates an Ed25519 key pair in the data directory. Clients use the public key to verify they are talking to your server and to encrypt the session:
sudo ls /opt/rustdesk/data
sudo cat /opt/rustdesk/data/id_ed25519.pub
db_v2.sqlite3 id_ed25519 id_ed25519.pub
wQfK3kJ0ZgqWbZ1xY2c9pL8m4nE6rT7sA5dV3hU1oI0=
Copy the single line from id_ed25519.pub. Keep id_ed25519 private and include the whole data directory in your backups: if the key changes, every client must be reconfigured.
Step 5 - Configuring the clients
On each machine running the RustDesk client:
- Open Settings > Network and unlock the network settings if prompted.
- Click ID/Relay server.
- Set ID server to
rustdesk.your_domainoryour_server_ip. - Leave Relay server empty (it defaults to the same host) or set it to the same value.
- Paste the public key into Key and click OK.
The status line at the bottom of the main window should read Ready. If it shows a connection error, go back to Step 2 and check the firewall.
To test, read the ID shown on one client and enter it in the other, then connect. The server logs show the registration and connection:
sudo docker compose -f /opt/rustdesk/compose.yaml logs --tail 20 hbbs
Step 6 - Setting up unattended access
For servers or remote workstations where nobody is present to accept connections, set a permanent password on the remote machine. In the client, open Settings > Security, unlock it, and under Password choose Use permanent password and set a strong one.
On Linux machines installed from the .deb package, the client runs as the rustdesk systemd service, so it survives reboots and user logouts. Confirm it is enabled:
systemctl status rustdesk --no-pager
NoteUnattended access on Linux works best with an X11 session. On Wayland, the local user usually has to approve screen sharing, so choose an Xorg session at the login screen if you need fully unattended access.
Step 7 - Updating the server
The server image is updated regularly. To upgrade, pull the new image and recreate the containers; the data volume and keys are preserved:
cd /opt/rustdesk
sudo docker compose pull
sudo docker compose up -d
Troubleshooting
Clients never reach the Ready state. Port 21116 is not reachable. From another machine, test TCP with nc -zv your_server_ip 21116, and make sure UDP 21116 is allowed in UFW and in any external firewall.
Key mismatch errors when connecting. The key in the client does not match id_ed25519.pub. Copy it again without extra spaces or line breaks. All clients must use the same ID server and key.
Connections are slow. The session is being relayed through hbbr instead of connecting directly, which happens when both sides are behind restrictive NAT. The relay needs enough bandwidth on the server; a direct connection only works if UDP hole punching succeeds.
Conclusion
You now run your own RustDesk ID and relay server, and your clients register and connect through it using your server's public key. Next, back up /opt/rustdesk/data, roll out the same ID server and key to every client your team uses, and restrict who can reach the server with firewall rules if all your devices come from known networks.
