Self-Hosting RustDesk: A Private Remote Desktop Server in Under 10 Minutes

RustDesk is an open-source remote desktop application written in Rust. Out of the box it connects through RustDesk's public relay infrastructure — fine for getting started, but that means your screen data passes through someone else's servers. Self-hosting changes that: all traffic relays through hardware you control, nothing leaves your network without your permission, and you can drop the paid subscription entirely.
The server is two small stateless binaries you can run comfortably on the same VPS or LXC container that hosts anything else in your homelab.
How the Server Works
RustDesk splits the server side into two roles:
(the ID / rendezvous server) — handles peer discovery. Devices register here so they can find each other, and it coordinates the initial NAT-traversal handshake.1hbbs
(the relay server) — when a direct peer-to-peer connection fails (most NAT combinations), traffic flows through here.1hbbr
Both are shipped in a single Docker image; you just pass a different command to each container.
Port Requirements
Open these on your server's firewall before starting the containers:
| Port | Protocol | Service | |------|----------|---------| | 21115 | TCP | hbbs — NAT-type test | | 21116 | TCP + UDP | hbbs — ID/rendezvous | | 21117 | TCP | hbbr — relay traffic | | 21118 | TCP | hbbs — WebSocket (web client only) | | 21119 | TCP | hbbr — WebSocket (web client only) |
Ports 21118 and 21119 are only needed if you want browser-based access. The native desktop client needs only 21115–21117.
Docker Compose Setup
Create a working directory and add a
:1docker-compose.yml
1mkdir -p ~/rustdesk/data && cd ~/rustdesk 2
1services: 2 hbbs: 3 image: rustdesk/rustdesk-server:latest 4 command: hbbs 5 ports: 6 - "21115:21115" 7 - "21116:21116" 8 - "21116:21116/udp" 9 - "21118:21118" 10 volumes: 11 - ./data:/root 12 restart: unless-stopped 13 depends_on: 14 - hbbr 15 16 hbbr: 17 image: rustdesk/rustdesk-server:latest 18 command: hbbr 19 ports: 20 - "21117:21117" 21 - "21119:21119" 22 volumes: 23 - ./data:/root 24 restart: unless-stopped 25
Both services share the same
volume so they read the same keypair. Bring it up:1./data
1docker compose up -d 2docker compose logs -f 3
On first start,
generates an Ed25519 keypair inside1hbbs
. Watch the logs until both services print1./data/
before configuring any clients.1Listening on
Grabbing the Public Key
Your clients need the server's public key to verify they're talking to your server and not a rogue relay.
1cat ~/rustdesk/data/id_ed25519.pub 2
This prints a single line of base64 text — copy it. You'll paste it into each client's settings in a moment. Keep
(the private key, no1id_ed25519
) secret and backed up; losing it means all existing clients need to be re-keyed.1.pub
Firewall Rules
If you're using
:1ufw
1sudo ufw allow 21115/tcp 2sudo ufw allow 21116/tcp 3sudo ufw allow 21116/udp 4sudo ufw allow 21117/tcp 5# Only needed for browser-based access: 6sudo ufw allow 21118/tcp 7sudo ufw allow 21119/tcp 8sudo ufw reload 9
For
(Rocky Linux / Fedora):1firewalld
1sudo firewall-cmd --permanent --add-port=21115-21119/tcp 2sudo firewall-cmd --permanent --add-port=21116/udp 3sudo firewall-cmd --reload 4
If your server is behind a cloud provider (AWS, Hetzner, DigitalOcean), also open the same ports in the provider's security group or firewall panel —
alone isn't enough if the provider has its own layer.1ufw
Configuring the RustDesk Client
Install the RustDesk desktop client on each machine you want to control or connect from. Then:
- Open RustDesk → click the three-dot menu (top-right) → Settings → Network.
- Unlock the panel if prompted.
- Under ID/Relay Server, fill in:
- ID Server: your server's IP address or hostname
- Relay Server: same as above (can leave blank if identical to ID Server)
- Key: paste the full contents of
1id_ed25519.pub
- Click OK. RustDesk reconnects and registers a fresh ID against your server.
Both the machine you're controlling from and the remote machine need to point at the same server. On a LAN you can use the private IP; for internet access use the public IP or a DNS record you control.
Verifying It Works
On the remote machine, note the RustDesk ID shown in the main window. On the controlling machine, type that ID into the Connect field and hit Enter. If the server is configured correctly the handshake completes in a second or two and you'll see the remote desktop.
Check relay logs if connections are slow or fail:
1docker compose logs hbbr --since=5m 2
You should see
lines appearing as connections negotiate. If you see nothing at all, the client is probably still hitting RustDesk's public servers — double-check the Key field was saved.1[relay] ...
A Note on Security
The Key field is the critical security control. Without it, anyone who discovers your server's IP can use your relay for free. With it, only clients that present the matching key are accepted.
Do not put these ports behind Nginx or Traefik — the RustDesk native client doesn't speak HTTP, so a reverse proxy can't terminate its connections. Direct port exposure on the server is the correct setup. If you want TLS, RustDesk Pro supports it; the open-source version does not.
Wrapping Up
Two containers, a handful of firewall rules, a public key distributed to your clients — that's the entire setup. I run my RustDesk server on the same small VPS as my n8n instance; together they use less than 50 MB of RAM at idle. No monthly fee, no third-party relay, full control over who can connect and when.
For personal and homelab use the open-source version above covers everything. If you need centralized user management, audit logs, and a web console, RustDesk also offers a self-hosted Pro tier — but start here first and upgrade only if you actually hit a limit.
