August 14, RustDesk released a preview build 1.4.9, which features real unattended access on Wayland — connecting to a machine with no one there, including the login screen after reboot. The news itself is small, but it nicely reminds why you would want to keep RustDesk on your own: remote access to home and work machines should not depend on someone else’s infrastructure or mood.
The problem is that documentation for self-hosting is scattered across about ten pages, and as a result a typical installation looks like “copied compose from a forum, seems to connect.” Below is collected in one place: what two daemons do, what each of the six ports is for, where the key comes from, what to open in the firewall, and where in the official docs the warning about IP spoofing is hidden.
Who needs it: for those who have a home NAS, a mini-PC, or a home lab, and need reliable access to them from a laptop; for those who help relatives with their computers; for those who don’t want identifiers and traffic to pass through the vendor’s public servers.
Part 1. Two daemons: who searches, and who forwards
A RustDesk server is not a single program, but two independent binaries (plus the utility rustdesk-utils):
- hbbs — identity server and rendezvous. Clients register their numeric ID with it, send heartbeat, where the NAT type is determined and two clients are connected to each other.
- hbbr — relay. It is enabled only when a direct connection between clients could not be established.
A key point to understand before installation: in a normal scenario your server only sees service communication, and the actual remote desktop stream goes directly between machines. The server becomes a bottleneck for traffic only in relay mode.
That’s why on a VPS with 1 vCPU and a symbolic bandwidth RustDesk usually runs smoothly — until both sides end up behind symmetric NAT or CGNAT. Then the entire video stream will go through the relay, and you’ll suddenly realize that 100 Mbps on the VPS wasn’t limitless.
If you have already read the analysis about gray IP and CGNAT — Серый IP и CGNAT: пять маршрутов к домашнему серверу — большой гайд — you’ll recognize half the mechanics: NAT punching here is exactly the same as in WireGuard-like solutions, only the coordinator role is played by hbbs.
Part 2. Six ports, and one of them for UDP
The most common mistake during deployment is opening the TCP range and forgetting UDP. After that, clients see each other, the connection is established, but always through the relay: NAT punching works over UDP.
| Port | Daemon | Purpose |
|---|---|---|
| 21114/tcp | hbbs | web console (Pro version only) |
| 21115/tcp | hbbs | NAT type test |
| 21116/tcp | hbbs | ID registration and heartbeat |
| 21116/udp | hbbs | NAT punching and connection establishment |
| 21117/tcp | hbbr | relay |
| 21118/tcp | hbbs | WebSocket for the web client |
| 21119/tcp | hbbr | WebSocket relay for the web client |
21116 is needed in both TCP and UDP — these are two separate channels on the same port. If your firewall rules only describe the TCP range 21114:21119, NAT punching will silently fail, and you’ll pay for traffic for every session.
Part 3. Deployment in Docker
The official image is rustdesk/rustdesk-server. Minimal docker-compose.yml from the documentation:
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
The same result without compose:
sudo docker run --name hbbs -v ./data:/root -td --net=host \
--restart unless-stopped rustdesk/rustdesk-server hbbs
sudo docker run --name hbbr -v ./data:/root -td --net=host \
--restart unless-stopped rustdesk/rustdesk-server hbbr
Here are two non-obvious details.
Why network_mode: host. NAT punching needs the server to see real addresses and ports of the clients. A classic bridge with -p 21116:21116/udp hides the source, and NAT type detection starts to lie. The downside is that containers occupy ports on the host, so running two installations on the same machine without additional tricks won’t work.
Directory ./data. It stores the SQLite database and, most importantly, a pair of keys. If you mount tmpfs here or forget about volumes, on every restart the server will generate a new key, and all clients will disconnect with a connection error.
Part 4. The key: pass-through, not just a checkbox for encryption
On first startup hbbs creates a key pair and places them in the working directory:
ls -1 ./data
# id_ed25519 — private, stays on the server
# id_ed25519.pub — public, entered into the client
# db_v2.sqlite3 — database
cat ./data/id_ed25519.pub
The contents of id_ed25519.pub are the exact string that is pasted into the client in the Key field.
The public key plays a double role here: it both provides encryption and acts as a pass-through. A client without the correct key will not be able to use your relay. If you publish the server address and key in a public chat, you’ll be distributing your channel to strangers — and the traffic will be charged to you.
The private id_ed25519 should not leave the server and should not end up in git along with the compose file.
Part 5. Client configuration
In the client (Settings → Network, requires elevation) four fields are filled:
- ID Server — required. The host or IP of your hbbs, optionally with a port:
srv.example.comorsrv.example.com:21116. - Key — required. The contents of
id_ed25519.pub. - Relay Server — usually not filled: the client learns the relay address from hbbs.
- API Server — not needed for OSS builds; required for login and web console, i.e. for Pro.
For ten machines manually — feasible. Beyond that there are options: export settings from an already configured client and import via clipboard, or run with a ready-made configuration string:
rustdesk.exe --config <config-string>
A custom client generator that embeds the server address and branding directly into the installer is already a paid feature.
Part 6. When traffic goes past the server, and when through it
By default, clients try to negotiate directly and switch to hbbr only if that fails. Sometimes a direct connection is undesirable — for example, when you don’t want to reveal your home IP to the person you’re giving access to. For this there is an environment variable:
environment:
- ALWAYS_USE_RELAY=Y
With it, all traffic will go through the relay regardless, even if a direct channel is possible. This requires a deliberate decision: latency will increase, and the outgoing VPS traffic will be consumed for every session. On cheap hosting with bandwidth limits, a full remote access in “always relay” mode can consume the monthly quota noticeably faster than it seems.
Part 7. Firewall and one line in the docs that everyone scrolls past
Basic rules from the official instructions:
ufw allow 21114:21119/tcp
ufw allow 21116/udp
sudo ufw enable
But further in the documentation there is a warning that should be read in full: with WebSocket enabled (ports 21118/21119) hbbs and hbbr trust the headers X-Real-IP and X-Forwarded-For in incoming WebSocket connections.
Do not expose 21118/21119 directly to the Internet. Anyone who reaches them can spoof these headers and pretend to be an arbitrary IP address — with all the consequences for logs, bans, and accounting.
How to do it properly:
- if a web client is not needed — keep 21118 and 21119 closed; they are not required for ordinary desktop clients;
- if needed — publish them only through a reverse proxy that itself sets
X-Real-IP, and on the firewall allow access to these ports only from the proxy address.
Part 8. What about Wayland and the 1.4.9 preview build
Now about the context. According to the official RustDesk blog post dated August 14, 2026, the preview build 1.4.9 introduced unattended access on Wayland: connection without manual confirmation on the other side, multi-monitor support, and access to the login screen after reboot.
Current limitations, also per the official announcement:
- a separate build, a preview; this has not yet arrived in standard releases;
- only Debian/Ubuntu-based systems with x86_64 architecture are supported;
- Fedora and Arch are planned.
As of publication, the stable release remains 1.4.7 from June 2, 2026. A preview build is what you put on a test machine, not on the server you access from vacation. The vendor’s statements about how everything is now good on Wayland have not yet been independently verified.
Why this is news: in Wayland there is no traditional global access to the screen and input as in X11; everything goes through portals and requires a session with interactive confirmation. That’s why “connecting to a machine with no one around” for a long time in most solutions either didn’t work or worked by reverting to X11.
Part 9. Where the free version ends
A fair boundary to avoid disappointment after installation:
- the web console on 21114, accounts, shared address book, custom client generator, SSO and session log — are Pro;
- in OSS you get a working rendezvous and relay, encryption by key, and everything the client does — but fleet management remains on you;
- client updates on all OSS machines will have to be rolled out manually.
For a single family, home lab, or small workshop, the OSS option is plenty. For a fleet of a hundred machines, the lack of a central console starts to feel quite quickly.
Sources
- Official documentation: rustdesk.com/docs — self-host, OSS, Docker
- rustdesk.com/docs — installing OSS server and firewall rules
- rustdesk.com/docs — configuring client for your own server
- RustDesk blog: unattended remote access on Wayland, August 14, 2026
- RustDesk client releases on GitHub
What do you use to secure remote access to home machines: RustDesk with its own server, WireGuard plus regular RDP/VNC, or something like Apache Guacamole? And if you ran RustDesk through a relay — how much traffic did an hour of operation consume?