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 sudo privileges.
  • Optionally, a domain name such as rustdesk.your_domain with a DNS A record pointing to your_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:

PortProtocolComponentPurpose
21115TCPhbbsNAT type test
21116TCP and UDPhbbsID registration, heartbeat and hole punching
21117TCPhbbrRelay
21118, 21119TCPhbbs, hbbrWeb 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:

  1. Open Settings > Network and unlock the network settings if prompted.
  2. Click ID/Relay server.
  3. Set ID server to rustdesk.your_domain or your_server_ip.
  4. Leave Relay server empty (it defaults to the same host) or set it to the same value.
  5. 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

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.