Jesse A. Green / notebook

RustDesk — Admin Guide

Generated September 2026 from rustdesk/rustdesk at a7f2260

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is for administrators, security reviewers, packagers and developers working with the RustDesk client 1.5.0 (commit a7f2260). It covers architecture, the complete configuration model, deployment and packaging, the security model, operations, performance tuning, integrations with a self-hosted server API, and developer internals. Day-to-day usage is in the companion User Guide.


1. Executive Summary

Item Summary
What it is Cross-platform remote desktop client and host: screen/audio streaming, input injection, file transfer, clipboard, terminal, TCP tunneling, camera view, recording
Built with Rust 2021 (MSRV 1.75) core in a Cargo workspace; Flutter UI (Dart SDK ^3.1.0) bridged with flutter_rust_bridge 1.80; legacy Sciter UI still buildable
Codecs libvpx (VP8/VP9), libaom (AV1), FFmpeg-based hardware H.264/H.265 (VAAPI, NVENC, AMF, QSV, VideoToolbox, MediaCodec), Windows D3D11 "VRAM" path; Opus audio
Network model Client registers with an ID/rendezvous server (hbbs, 21116); sessions race TCP/UDP-KCP/IPv6/WebRTC hole punching and fall back to a relay (hbbr, 21117)
Crypto Ed25519 identities, X25519 box key exchange, XSalsa20-Poly1305 secretbox session encryption, per-direction subkeys (kx v1)
Platforms Windows (x64, arm64, x86 Sciter), macOS 10.14+, Linux (deb/rpm/Arch/AppImage/Flatpak), Android 5.1+, iOS 13+ (controller only)
Deployment shape Desktop: privileged OS service + per-user server + tray + connection manager processes. No server component in this repo — the ID/relay servers are separate projects.
Licence GNU AGPL-3.0 (the RPM specs' GPL-3.0 field is inconsistent with LICENCE)
Operational burden Low for clients (auto-update on installed Windows, update checks elsewhere). Real work is in server choice, key distribution, policy (permissions, approve mode, 2FA) and Linux display-server constraints.

Important

The default configuration uses the public ID server rs-ny.rustdesk.com with key OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=. Organizations that need to control metadata and relay paths should deploy their own server and distribute custom-rendezvous-server, relay-server, api-server and key to every client.


2. Architecture

2.1 System overview

                          +----------------------------+
                          |  ID / rendezvous (hbbs)    |  TCP+UDP 21116, TCP 21115 (NAT test)
                          |  optional HTTP API 21114   |  WebSocket 21118
                          +-------------+--------------+
                 RegisterPeer/Pk (15 s) |  PunchHole / FetchLocalAddr / RequestRelay
        +-------------------------------+-------------------------------+
        |                                                               |
+-------v---------------------------+                   +---------------v-------------------+
|  CONTROLLER (client)              |                   |  CONTROLLED (host)                 |
|  Flutter UI  <-FFI->  librustdesk |  race: TCP punch  |  --service (SYSTEM/root)           |
|   session / io_loop / decoders    |<=== UDP+KCP ====> |    supervises --server             |
|   port_forward, file jobs         |     IPv6+KCP      |  --server (user): RendezvousMediator|
|                                   |     WebRTC        |    Connection, services:           |
+-------------------+---------------+                   |    monitorN, audio, clipboard,     |
                    |      fallback                     |    input, display, printer,        |
                    |   +----------------------+        |    terminal (per connection)       |
                    +==>|  relay (hbbr) 21117  |<=======+  --cm   Connection Manager (UI)    |
                        |  WS relay 21119      |        |  --tray tray icon                  |
                        +----------------------+        +------------------------------------+

2.2 Component inventory

Component Path Role
Entry & CLI dispatch src/main.rs, src/core_main.rs Argument parsing, process roles, install/uninstall commands
Rendezvous client src/rendezvous_mediator.rs Registration, NAT test, punch/relay handling on the host, direct-IP listener
Client connection src/client.rs, src/client/io_loop.rs Punch/relay race, secure handshake, login, decode, session I/O
Server core src/server.rs, src/server/* Service registry, per-connection handling (connection.rs), video/audio/input/clipboard/display/printer/terminal services
Media capture & codecs libs/scrap DXGI/GDI/Magnifier, Quartz, X11 SHM, Wayland PipeWire, DRM/KMS, Android; VPX/AOM/hwcodec/VRAM encoders; recording
Input injection libs/enigo, src/keyboard.rs, src/server/uinput.rs, src/server/rdp_input.rs Keyboard modes, uinput devices, portal input
Clipboard src/clipboard.rs, src/clipboard_file.rs, libs/clipboard Text/image/HTML sync, file copy-paste (CLIPRDR, FUSE, macOS placeholder)
Shared core libs/hbb_common (git submodule) Config, protobuf protocol, sockets, TLS, proxy, WebSocket, WebRTC, password security
Client-only base libs/base Option keys (config/keys.rs), message protocol, file-transfer engine (fs.rs), platform helpers
HTTP API client src/hbbs_http/* Heartbeat/sysinfo sync, OIDC account login, downloader, record upload, HTTP client
IPC src/ipc.rs, src/ipc/* Local IPC between service, server, UI, CM, tray
Platform layer src/platform/* Windows/macOS/Linux service install, privileges, session handling
Privacy mode src/privacy_mode* Windows exclude-from-capture / magnifier / virtual display, macOS gamma + event tap
Virtual display libs/virtual_display, src/virtual_display_manager.rs Amyuni IDD (selected) / RustDesk IDD on Windows 10 19041+
Remote printer libs/remote_printer, src/server/printer_service.rs Windows virtual printer + job forwarding
Whiteboard src/whiteboard --whiteboard overlay process for "show my cursor"
Flutter UI flutter/lib Desktop and mobile UI (desktop/, mobile/, common/, models/)
FFI bridge src/flutter_ffi.rs, src/flutter.rs ~315 functions exposed to Dart
Legacy UI src/ui/*, src/ui.rs Sciter HTML/TIS UI (deprecated)
Portable packer libs/portable Windows self-extracting executable
Packaging res/, appimage/, flatpak/, res/msi deb/rpm/Arch/AppImage/Flatpak/MSI/systemd/launchd

2.3 Cargo workspace layers

Layer Crates Notes
Application rustdesk (lib librustdesk as cdylib/staticlib/rlib; bins rustdesk, naming, service) default-run = "rustdesk"; service is the macOS launchd daemon binary
Shared protocol & config libs/hbb_common Submodule shared with the server; changes cost a server round-trip, so client-only code goes to libs/base
Client platform libs/base, libs/enigo, libs/clipboard, libs/scrap
Windows extras libs/virtual_display, libs/virtual_display/dylib, libs/remote_printer, libs/portable
Stubs & patches libs/libxdo-sys-stub (patched in for libxdo-sys), forked tungstenite, webrtc, webrtc-util, webrtc-sctp libxdo is loaded at runtime; missing libxdo makes X11 input a no-op

2.4 Process model

Process Started by Runs as Purpose
rustdesk --service (macOS: .../MacOS/service) OS service manager SYSTEM / root Supervisor; owns _service IPC, launches --server in the active session, handles SAS, uinput, DRM export
rustdesk --server service, launchd agent, or main UI fallback user (root at Linux login screen) Owns main IPC, rendezvous registration, accepts sessions
rustdesk --tray UI / server / Startup shortcut user Tray menu Open, Stop service; tooltip with session count
rustdesk --cm server on first incoming connection user Connection Manager window
main UI user user Outgoing sessions and settings; runs server logic in-process when no --server is reachable
portable service Quick-support / not-installed Windows elevated / SYSTEM Capture + input across UAC via shared memory
--terminal-helper server (Windows) user ConPTY host for remote terminals
--whiteboard server user Cursor/annotation overlay

IPC endpoints: Windows named pipe \\.\pipe\RustDesk\query<postfix>; Linux/macOS /tmp/RustDesk-<euid>/ipc<postfix> (the _service and _uinput_* postfixes live in /tmp/RustDesk-service/). Postfixes: "" (main), _service, _cm, _pa (PulseAudio on Linux), _url (macOS), _whiteboard, _portable_service, _uinput_*, _drm.

Note

If a --server cannot bind the main IPC, it logs ipc is occupied by another process, try kill it, kills the main-window process and exits. Non-server processes pull configuration from the server over IPC at start (Data::SyncConfig), so the --server process is the source of truth for options at runtime.

2.5 Connection establishment

  1. Controller guards: incoming-only builds refuse (Incoming only mode).
  2. Direct IP or domain:port targets skip rendezvous and connect by TCP (bare IP → port 21118).
  3. In parallel the controller prepares an IPv6 probe, a UDP NAT socket, and a WebRTC offer (disabled with a proxy).
  4. The rendezvous socket is secured (secure_tcp) when a WebRTC offer rides on it (mandatory) or when a key plus token/switch code exists.
  5. PunchHoleRequest{id, token, nat_type, licence_key, conn_type, version, udp_port, force_relay, socket_addr_v6, switch_code, webrtc_sdp_offer} goes to hbbs.
  6. The host reacts to PunchHole (UDP/TCP punch, IPv6, WebRTC answer), FetchLocalAddr (same LAN) or RequestRelay.
  7. The controller races TCP, UDP/KCP, IPv6/KCP and WebRTC; direct timeouts are 1 s on LAN or symmetric NAT, else punch time ×6 (×3 after a previous direct failure).
  8. On failure or forced relay: RequestRelay → TCP to hbbr 21117 with the shared UUID.
  9. Secure handshake (section 6), then login.
Constant Value
RENDEZVOUS_TIMEOUT 12 000 ms
CONNECT_TIMEOUT / READ_TIMEOUT 18 000 ms
REG_INTERVAL 15 000 ms
Punch attempts 3 (deadlines 3 s, 6 s, 9 s)
Relay attempts 3
RELAY_FALLBACK_DELAY_MS 2 500 (option relay-fallback-delay)
Host punch caps 32 UDP + 32 TCP in flight, 16 WebRTC answerers
Unauthenticated connections 64 total, 16 per IP, 128 KiB max message, 180 s login grace

Labels shown to users: TCP, UDP, IPv6, WebRTC, WebRTC/IPv6, Relay, WebSocket. direct = false for relay, WebSocket and TURN-relayed WebRTC.

2.6 Service model on the host

Services are GenericService threads keyed by name: audio, display, clipboard, file-clipboard (feature), mouse_cursor, mouse_pos, window_focus (desktop; X11 only on Linux), remote-printer (Windows Flutter), and lazily created monitor<N> / camera<N> video services. Terminal services are per connection.

  • A connection subscribes only to services its permissions allow (for example no audio if audio is disabled, no clipboard without clipboard permission).
  • The run() loop backs off from 30 ms to 1000 ms on errors and logs Error of {name} service: {err}.
  • Any change needing a new capturer/encoder raises SWITCH, which restarts the video service (codec change, new subscriber, quality change on encoders that cannot change on the fly, privacy-mode change, recording change).

2.7 Caching

Cache Location / scope Behaviour
Hardware codec capability config file RustDesk_hwcodec (Windows/macOS) Written by the --check-hwcodec-config child; reused while the GPU signature is unchanged
AV1 speed test option av1-test (Y/N) 10 synthetic 1080p frames at startup; key frame < 90 ms and deltas < 30 ms to pass
TLS mode per host:port in memory rustls vs native-tls and invalid-cert acceptance; cleared when proxy/TLS options change
Address book / group RustDesk_ab, RustDesk_group Compressed, encrypted JSON; max 64 MiB; deleted if unreadable
Peer list peers/*.toml Sorted newest first, loaded in batches of 100; preloaded on a background thread
Cursor shapes in memory 4096 handles / 32 MB
Wayland consent LocalConfig wayland-restore-token ScreenCast portal v4+ with persist_mode = 2
Login failures in memory (LOGIN_FAILURES) Lost on restart

Note

Values equal to a default (including a custom-client default) are never written to disk, and options present in an override layer are removed from disk. Config files therefore do not show effective defaults; read effective values with rustdesk --option <key>.


3. System Requirements

3.1 Runtime platforms

Platform Minimum / notes
Windows 64-bit x64 or arm64 Flutter builds; 32-bit x86 Sciter build. System audio loopback needs Windows 10 1703+. Virtual displays need Windows 10 19041 (2004)+. Remote printer and "show my cursor" need Windows 10+. HDR output is converted to SDR (SDR white level needs 1709+).
macOS 10.14 deployment target. Needs Screen Recording, Accessibility and Input Monitoring permissions; launchd daemon + agent for unattended access. ScreenCaptureKit is used only for audio.
Linux X11 XCB MIT-SHM capture; libxdo loaded at runtime (missing = no X11 input); PulseAudio monitor source for audio
Linux Wayland xdg-desktop-portal + PipeWire + GStreamer; Ubuntu 21.04+; consent dialog unless ScreenCast restore token; GDM Wayland greeter only with the drm build (rustdesk-unattended-wayland)
Android minSdk 22, target/compile SDK 36; audio capture from SDK 30 (Android 11); accessibility service for input
iOS 13.0+; controller only

3.2 Linux runtime dependencies (Debian control)

libgtk-3-0t64 | libgtk-3-0, libxcb-randr0, libxdo3 | libxdo4, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2t64 | libasound2, libsystemd0, curl, libva2, libva-drm2, libva-x11-2, libgstreamer-plugins-base1.0-0, gstreamer1.0-pipewire (+ libatomic1 on armhf); Recommends libayatana-appindicator3-1. The DRM variant adds libdrm2 (>= 2.4.101), libegl1, libgles2 and a measured glibc floor.

3.3 Build toolchain

Tool Version (CI pins)
Rust 1.75 (Sciter/i128 ABI constraint); macOS 1.81 (cidre)
Flutter 3.24.5 (Windows arm64: 3.44.9; Linux arm64: flutter-elinux 3.16.9)
flutter_rust_bridge 1.80.1 (codegen needs cargo-expand 1.0.95)
vcpkg commit 9e593bb18ea69cc5095e012465dcd675a822ed0d (baseline in vcpkg.json)
Android NDK r28c; cargo-ndk 3.1.2
LLVM 15.0.6
vcpkg overlay ports aom 3.14.1, ffmpeg 7.1.1 (13 patches), libvpx 1.15.2, libyuv 1857, mfx-dispatch 1.35.1, opus 1.5.2

3.4 Hardware guidance

Resource Guidance from the code
RAM On hosts with ≤ 4 GB total RAM, auto codec selection prefers VP8 over VP9/AV1
CPU Encoder threads = idle-CPU based, ≤ CPUs/2 and ≤ free GB/2, rounded to a power of two (VPX/AOM max 64, FFmpeg max 16). AV1 disabled on 32-bit builds and on hosts failing the self-test.
GPU Hardware H.264/H.265 via FFmpeg hwcodec; Windows VRAM path needs DXGI capture + enable-hwcodec (+ allow-d3d-render on the viewer)
Network VP8/VP9 target at 1920×1080 = 2073 kbps base × quality ratio: ≈ 1.4 Mbps Balanced (0.67), ≈ 1.0 Mbps Optimize reaction time (0.5), ≈ 3.1 Mbps Good image quality (1.5); adaptive bitrate moves the ratio between 0.1 and 40

4. Configuration Mastery

4.1 Files and locations

All files are {APP_NAME}{suffix}.toml (TOML via confy, mode 0600 on Unix). APP_NAME defaults to RustDesk and changes with a custom client's app-name.

Struct File Contents
Config RustDesk.toml enc_id (encrypted ID), password (hashed), salt, key_pair, key_confirmed, keys_confirmed
Config2 RustDesk2.toml rendezvous_server, nat_type, serial, unlock_pin (encrypted), trusted_devices (encrypted), socks (proxy), options (all server/"Settings" options)
LocalConfig RustDesk_local.toml remote_id, kb_layout_type, window size, fav, options (local UI options), ui_flutter
UserDefaultConfig RustDesk_default.toml Display defaults for new peers (re-read at most every 1 s)
Status RustDesk_status.toml Free key/value status
LanPeers RustDesk_lan_peers.toml Discovered LAN peers
PeerConfig peers/<id>.toml Per-peer session settings; IDs with `< > : / \
Address book / group cache RustDesk_ab, RustDesk_group Encrypted compressed JSON
OS Config directory Log directory
Windows user %APPDATA%\RustDesk\config %APPDATA%\RustDesk\log
Windows service C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config (any system32\config\systemprofile path is rewritten) ...\RustDesk\log
Linux ~/.config/rustdesk (a root process whose whoami is not root uses that user's home) ~/.local/share/logs/RustDesk
macOS ~/Library/Preferences/com.carriez.RustDesk (Application Support rewritten to Preferences) ~/Library/Logs/RustDesk
Android app dir passed from Java <home>/RustDesk/Logs

Note

The Windows and Linux folder names come from the directories_next crate's conventions combined with RustDesk's path rewrites. There is no environment variable that relocates the config directory.

4.2 Resolution layers

Layer Storage Lookup order
Server / Settings options RustDesk2.toml [options] OVERWRITE_SETTINGS → file → DEFAULT_SETTINGS
Local options RustDesk_local.toml [options] OVERWRITE_LOCAL_SETTINGS → file → DEFAULT_LOCAL_SETTINGS
Display defaults RustDesk_default.toml [options] OVERWRITE_DISPLAY_SETTINGS → file → DEFAULT_DISPLAY_SETTINGS → per-key validation
Built-in (custom client) memory BUILTIN_SETTINGS Custom client only; default ""
Hard settings (custom client) memory HARD_SETTINGS Custom client only

Boolean convention (option2bool):

Key pattern True when Default when empty
enable-* value ≠ N on
allow-*, stop-service, direct-server, force-always-relay value = Y off
anything else value ≠ N on

4.3 Server / Settings options (RustDesk2.toml)

Key Values Default Effect
access-mode full, view, other = custom custom Flutter desktop: full forces all permissions on, view all off
enable-keyboard Y/N on Keyboard/mouse control
enable-clipboard Y/N on Clipboard (also needs keyboard permission)
enable-file-transfer Y/N on File transfer sessions and file clipboard
enable-camera Y/N on Camera view sessions
enable-terminal Y/N on Terminal sessions
enable-remote-printer Y/N on Print jobs to the controller
enable-audio Y/N on Audio stream
enable-tunnel Y/N on TCP tunnels / RDP
enable-remote-restart Y/N on Restart remote device
enable-record-session Y/N on Controller may record
enable-block-input Y/N on Block user input (Windows)
enable-privacy-mode Y/N on Privacy mode
enable-lan-discovery Y/N on Answer LAN discovery pings
enable-trusted-devices Y/N on Trusted devices skip 2FA
enable-abr Y/N on Adaptive bitrate
enable-hwcodec Y/N on Hardware codecs + probe process
enable-directx-capture Y/N on Windows DXGI capture (needed for VRAM encode)
enable-android-software-encoding-half-scale Y/N on Android half-scale software encode
keep-awake-during-incoming-sessions Y/N on Keep host awake
approve-mode password, click, other = both both How sessions are approved
verification-method use-temporary-password, use-permanent-password, other = both both Accepted passwords
temporary-password-length 6, 8, 10 6 (any other value) One-time password length
allow-numeric-one-time-password Y off Digits-only one-time password
allow-hide-cm Y off Hide CM (only with approve=password + permanent-only)
allow-remote-config-modification Y off Unblocks the host's own settings UI during remote input (UI-only enforcement)
allow-auto-disconnect / auto-disconnect-timeout Y / minutes off / 10 Idle disconnect
allow-only-conn-window-open Y off Refuse when main window not running
allow-auto-record-incoming Y off Record incoming sessions
windows-service-video-save-directory absolute path %SystemDrive%\ProgramData\<app>\recording Recording dir for Windows service
allow-remove-wallpaper Y off Remove wallpaper during sessions
allow-always-software-render Y off Linux: sets LIBGL_ALWAYS_SOFTWARE=1
allow-auto-update Y off Automatic update (installed Windows / macOS)
allow-scope-violation-close / -alarm Y off Close / audit on out-of-scope messages
allow-websocket Y off WebSocket transport (relay only)
allow-insecure-tls-fallback Y off Accept invalid TLS certs after failure
allow-kcp-congestion-control / allow-webrtc-congestion-control Y off Transport congestion control
disable-udp Y off TCP-only rendezvous
direct-server / direct-access-port Y / port off / 21118 Direct IP listener
whitelist comma list of IP/CIDR; 0.0.0.0 = all empty = all IP allow-list
id-whitelist comma list, */? wildcards empty = all ID allow-list
custom-rendezvous-server host[:port] public ID server
relay-server host[:port] server-supplied / ID host +1 Relay
api-server URL derived HTTP API
key base64 Ed25519 public key RS_PUB_KEY Server key
ice-servers comma list stun:/turn: 4 default STUN WebRTC ICE servers
preset-address-book-*, preset-device-username, preset-device-name, preset-note strings empty Sent to the API server with sysinfo
stop-service Y off Service stopped (no registration, no direct server, no heartbeat)
2fa, bot encrypted JSON / token empty TOTP secret and Telegram bot token

4.4 Local options (RustDesk_local.toml)

Key Default Effect
theme, lang unset UI theme and language (pt/br read as pt-br)
enable-tcp-punch on TCP hole punching
enable-udp-punch, enable-ipv6-punch, enable-webrtc on for *.rustdesk.com; N when unset on self-hosted servers P2P transports
relay-fallback-delay 2.5 s WebRTC preference window
enable-port-forward-mux on Multiplex tunnels
allow-d3d-render off Windows VRAM decode
use-texture-render platform-dependent Flutter texture rendering
video-save-directory platform default Recording directory
allow-auto-record-outgoing off Record outgoing sessions
enable-check-update on Update check at startup
keep-awake-during-outgoing-sessions on Keep controller awake
allow-sync-clipboard-between-sessions off Push clipboard to all sessions
allow-remote-cm-modification off Remote CM changes
pre-elevate-service — Windows quick-support pre-elevation
enable-confirm-closing-tabs, enable-open-new-connections-in-tabs on Tab behaviour
printer-incomming-job-action (sic), allow-printer-auto-print, printer-selected-name — Incoming print jobs
hide-recording-button — Hide the recording button
wayland-restore-token — Remembered Wayland screen selection

4.5 Display defaults (RustDesk_default.toml)

Key Default Allowed
view_style original (desktop), adaptive (mobile) original, adaptive
scroll_style scrollauto scrollauto, scrollbar, scrolledge
edge-scroll-edge-thickness 100 20–150
image_quality balanced best, balanced, low, custom
custom_image_quality 50 10–4095
custom-fps 30 5–120
codec-preference auto vp8, vp9, av1, h264, h265
trackpad-speed 100 10–1000
enable-file-copy-paste Y "", N
Booleans per user default view_only, show_monitors_toolbar, collapse_toolbar, show_remote_cursor, follow_remote_cursor, follow_remote_window, zoom-cursor, show_quality_monitor, disable_audio, disable_clipboard, lock_after_session_end, privacy_mode, touch-mode, i444, reverse_mouse_wheel, swap-left-right-mouse, displays_as_individual_windows, use_all_my_displays_for_the_remote_session, terminal-persistent, sync-init-clipboard

Per-peer only: force-always-relay, rdp_port (3389), rdp_username, rdp_password, os-username, os-password (the last four encrypted).

4.6 Custom-client settings

A signed custom.txt (next to the executable; macOS ../Resources/custom.txt) is base64 of an Ed25519-signed JSON verified with the embedded key 5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=. app-name renames the app (service name, IPC paths, URI scheme, file names); default-settings and override-settings fill the DEFAULT/OVERWRITE maps (keys normalized _→-, routed to display, local, server or built-in layers); any other top-level key becomes a hard setting.

Built-in settings (custom client only):

Key Effect
hide-general-settings, hide-security-settings, hide-network-settings, hide-server-settings, hide-proxy-settings, hide-remote-printer-settings, hide-websocket-settings Hide settings pages/sections
hide-tray, hide-stop-service No tray icon (except macOS) / no Stop service
hide-help-cards, hide-username-on-card, hide-powered-by-me, remove-preset-password-warning, main-window-always-on-top, hide-elevate-button-in-accept-window UI
display-name, avatar Identity shown to peers
default-connect-password Password used when none saved
one-way-clipboard-redirection, one-way-file-transfer Directional restrictions
allow-logon-screen-password Permanent password at logon/lock screen even in click mode
allow-deep-link-password, allow-deep-link-server-settings Mobile rustdesk://password / config links
allow-https-21114 Keep :21114 on an https API URL
allow-hostname-as-id Hostname becomes the ID
register-device N = no registration, no API, no --assign/--deploy
file-transfer-max-files N>0 limit; 0 = 10 000; unset = unlimited
disable-change-permanent-password, disable-change-id, disable-unlock-pin Block those changes (UI and CLI)
use-raw-tcp-for-api Tunnel API calls over hbbs TCP
enable-perm-change-in-accept-window Allow permission toggles in the accept window
allow-command-line-settings-when-settings-disabled Allow --password/--set-id/--option when settings are disabled
preset-device-group-name, preset-user-name, preset-strategy-name Sent to the API server

Hard settings: conn-type (incoming/outgoing), disable-tcp-listen, disable-settings, disable-ab, disable-account, disable-installation, preset password + salt.

Important

Built-in and hard settings cannot be set through the UI, --option, or a config file — only through a correctly signed custom.txt. A hand-edited file fails signature verification and is ignored (logged only).

4.7 Servers and ports

Constant Value
RENDEZVOUS_SERVERS ["rs-ny.rustdesk.com"]
RS_PUB_KEY OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=
RENDEZVOUS_PORT / RELAY_PORT 21116 / 21117
WS_RENDEZVOUS_PORT / WS_RELAY_PORT 21118 / 21119
Default API https://admin.rustdesk.com
Default STUN stun.cloudflare.com:3478, stun.l.google.com:19302, stun.antisip.com:3478, stun.nextcloud.com:443

Rendezvous server selection: Windows exe-name host → custom-rendezvous-server → compile-time PROD_RENDEZVOUS_SERVER (empty) → remembered lowest-latency server → built-in list; :21116 appended if no port.

Derived service Rule
NAT test ID host:port-1 (21115)
Relay relay-server, else server-supplied, else ID host:port+1
Direct IP listener direct-access-port, else 21118
API api-server → exe-name api → http://<id host>:<port-2> (21114) → https://admin.rustdesk.com; https://…:21114 loses :21114 unless allow-https-21114
WebSocket (when allow-websocket) IPs: ws://ip:21118 / :21119; domains: ws(s)://domain/ws/id and /ws/relay; wss only when api-server starts with https

4.8 Environment variables

Variable Scope Effect
RUSTDESK_FORCED_DISPLAY_SERVER Linux Override display-server detection (x11/wayland); forwarded to the user server
XDG_SESSION_ID, XDG_SESSION_TYPE, XDG_CURRENT_DESKTOP, DESKTOP_SESSION Linux Session and desktop detection fallbacks
DISPLAY, XAUTHORITY Linux Set by the service before starting the X11 server
WAYLAND_DISPLAY, WAYLAND_SOCKET Linux Wayland endpoint
FLATPAK_ID Linux Flatpak mode (flatpak-spawn for loginctl)
APPDIR AppImage Relaunch via $APPDIR/AppRun
SHELL, LANG, LC_ALL, LC_CTYPE, TERM, TMUX, STY Unix terminal Shell and locale
COMSPEC Windows terminal Shell fallback
NO_DIALOG, PRINT_OUT, WRITE_TO_FILE Windows Suppress/print/write message boxes
SET_FOREGROUND_WINDOW Windows 1 forces foreground
RUSTDESK_APPNAME Windows portable Exe name used for licence parsing
IS_TERMINAL_ADMIN client Request an elevated terminal
rdp_username, rdp_password client (Windows) RDP credentials passed to cmdkey
LIBGL_ALWAYS_SOFTWARE Linux Flutter Set from allow-always-software-render
DISABLE_RUSTDESK_RESTORE_WINDOW_POSITION Flutter Skip window position restore
PULSE_LATENCY_MSEC=60, PIPEWIRE_LATENCY=1024/48000 systemd unit Audio latency

No environment variable overrides the ID server, relay, API or key.

4.9 Import and export

Mechanism Format Notes
UI export/import JSON {host, relay, api, key} → base64url → reversed Clipboard; plain JSON also accepted on import
QR code (mobile) config=<export string> Mobile scan only; the app never generates a config QR code
Deep link rustdesk://config/<string> Mobile, needs built-in allow-deep-link-server-settings
--config <string> host=...,key=...,api=...,relay=... or encoded form Installed + admin; silent
--option <key> [value] raw option Installed + admin; blocked by disable-settings
--import-config <path> RustDesk.toml + sibling RustDesk2.toml Copies only if newer; used by the installer
Windows exe name rustdesk-host=H,key=K,api=A,relay=R,.exe or rustdesk-licensed-<reversed b64>.exe Windows only; relay persisted only via --config

5. Deployment

5.1 Deployment options

Channel Artifact Service installed Best for
Windows EXE (self-extracting) rustdesk-<ver>-<arch>.exe On Install / --silent-install Ad-hoc and scripted installs
Windows MSI rustdesk-<ver>-<arch>.msi Yes (custom action) GPO / Intune / SCCM
Windows MSI template rustdesk-template-<arch>.msi Yes Per-customer branded MSI (second cabinet with custom.txt, icons)
Debian/Ubuntu rustdesk-<ver>-<arch>.deb systemd (/usr/lib/systemd/system) Linux fleets
Debian unattended Wayland rustdesk-unattended-wayland-<ver>.deb systemd Wayland login screens (x86_64)
RPM rustdesk-<ver>-*.rpm, *-suse.rpm systemd (/etc/systemd/system) Fedora/RHEL/openSUSE
Arch rustdesk-<ver>-*.pkg.tar.zst systemd (/etc/systemd/system) Arch/Manjaro
AppImage / Flatpak *.AppImage, *.flatpak, Flathub com.rustdesk.RustDesk No Per-user, outgoing-first
macOS rustdesk-<ver>-<arch>.dmg launchd daemon + agent via in-app Install Mac fleets
Android APKs per ABI, universal APK, F-Droid Android foreground service Phones/tablets

5.2 Windows EXE installation layout

Element Value
Install directory %ProgramFiles%\RustDesk (32-bit build prefers Program Files (x86))
Service sc create RustDesk binpath= "\"<exe>\" --service" start= auto DisplayName= "RustDesk Service" (skipped if outgoing-only or stop-service=Y)
Uninstall key HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\RustDesk with UninstallString "<exe>" --uninstall, WindowsInstaller 0
URI / file association HKCR\rustdesk URL protocol → "<exe>" "%1"; HKCR\.rustdesk\shell\open\command → "<exe>" --play "%1"
Firewall netsh advfirewall firewall add rule name="RustDesk Service" inbound and outbound allow for the exe
SAS policy HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System SoftwareSASGeneration=1
Shortcuts %PUBLIC%\Desktop\RustDesk.lnk (desktopicon), Start menu RustDesk.lnk + Uninstall RustDesk.lnk (startmenu), Startup RustDesk Tray.lnk (--tray)
Install choices Stored under HKCR\.rustdesk
Config at install A temporary service runs --import-config "<config path>" then is deleted
Exe-name licence key, custom-rendezvous-server, api-server persisted when both key and host exist
rustdesk-1.5.0-x86_64.exe --silent-install printer=1
(Toast "Installation Successful!"; with the debug argument the batch window stays open for 300 s.)

5.3 Windows MSI

Public properties for msiexec:

Property Default Effect
INSTALLFOLDER registry value or ProgramFiles6432Folder\<Product> Install directory (normalized to end in \<Product>\)
CREATESTARTMENUSHORTCUTS registry / 1 1/Y/y keeps Start-menu shortcuts
CREATEDESKTOPSHORTCUTS registry / 1 Desktop shortcut
INSTALLPRINTER registry / empty 1/Y/y installs the RustDesk printer (Windows 8.1+ VersionNT >= 603)
LAUNCH_TRAY_APP Y Launch --tray after install
STOP_SERVICE read from the user's RustDesk2.toml on fresh install; service state on upgrade Y skips service creation
CC_CONNECTION_TYPE set by preprocess.py --conn-type outgoing skips service, tray shortcut and SAS

Custom actions: CreateStartService, AddFirewallRules / RemoveFirewallRules, AddRegSoftwareSASGeneration, InstallPrinter / UninstallPrinter, TerminateProcesses, TerminateBrokers, TryStopDeleteService, RemoveRuntimeGeneratedFiles, RemoveAmyuniIdd, TryDeleteStartupShortcut, LaunchApp (non-silent only). UpgradeCode = uuid5(NAMESPACE_OID, "<app>.exe"), so upgrades are stable per app name; MajorUpgrade allows same-version upgrades.

Build the MSI: in res/msi, python preprocess.py [-d ../../rustdesk] [--app-name X] [-v X.Y.Z] [--conn-type incoming|outgoing] [--template] [--custom-arp JSON] [-m Manufacturer], then build msi.sln with Visual Studio 2022.

Warning

MSI and EXE installs are mutually exclusive: the MSI blocks installation over an EXE install (BlockSelfInstalledApp), and install_me refuses to run over an MSI install. Pick one installer type per fleet.

5.4 Linux packages

Package Service file Post-install
deb /usr/lib/systemd/system/rustdesk.service Symlink /usr/bin/rustdesk → /usr/share/rustdesk/rustdesk; daemon-reload, enable, start (systemd only); pkill rewritten to /usr/bin/pkill
rpm (Flutter) /etc/systemd/system/rustdesk.service Desktop files, symlink, enable/start, update-desktop-database
Arch /etc/systemd/system/rustdesk.service Same via pacman_install hooks

Unit file (res/rustdesk.service):

[Unit]
Description=RustDesk
Requires=network.target
After=systemd-user-sessions.service

[Service]
Type=simple
ExecStart=/usr/bin/rustdesk --service
ExecStop=pkill -f "rustdesk --"
PIDFile=/run/rustdesk.pid
KillMode=mixed
TimeoutStopSec=30
User=root
LimitNOFILE=100000
Environment="PULSE_LATENCY_MSEC=60" "PIPEWIRE_LATENCY=1024/48000"

[Install]
WantedBy=multi-user.target

The service loop runs --server as root at the login screen (or a Wayland greeter with the DRM build) and as the logged-in user otherwise, with DISPLAY, XAUTHORITY and TERM set. RustDesk actively removes ~/.config/autostart/rustdesk.desktop. A binary is considered "installed" when it lives under /usr or /nix/store.

Note

Non-systemd init systems get only the /usr/bin/rustdesk symlink from the deb; no service is installed. apt purge removes /root/.config/rustdesk only — user configs remain.

5.5 macOS

Element Value
App /Applications/RustDesk.app (bundle id com.carriez.rustdesk)
LaunchDaemon /Library/LaunchDaemons/com.carriez.RustDesk_service.plist → .../MacOS/service, KeepAlive, logs /var/log/rustdesk_service.err / .out
LaunchAgent /Library/LaunchAgents/com.carriez.RustDesk_server.plist → RustDesk --server in LoginWindow and Aqua sessions
Install Embedded install.scpt (admin prompt), copies user prefs to /var/root/Library/Preferences/com.carriez.RustDesk/
Uninstall Unloads daemon, removes plists, sets stop-service=Y
Update update.scpt replaces the bundle with ditto, strips quarantine, reloads

rustdesk --install-service on macOS only checks that both plists exist; install through the in-app Install button.

5.6 Docker

The repository Dockerfile builds a build container, not a runtime service:

Item Value
Base image debian:trixie-slim
Toolchain apt build deps, CMake 3.30.6 from source, vcpkg tag 2023.04.15 (libvpx libyuv opus aom), rustup default toolchain, Sciter libsciter-gtk.so
User USER root at runtime (HOME=/home/user)
Entrypoint entrypoint.sh: cd $HOME/rustdesk; copies Sciter lib into target/{debug,release}; --target X adds the rustup target; runs VCPKG_ROOT=/vcpkg cargo build --locked $@
docker build -t rustdesk-builder .
docker run --rm -it -v "$PWD":/home/user/rustdesk \
  -v rustdesk-git-cache:/home/user/.cargo/git \
  -v rustdesk-registry-cache:/home/user/.cargo/registry \
  rustdesk-builder --release
    Finished release [optimized] target(s)

Caution

There is no EXPOSE, VOLUME or CMD; the image does not run RustDesk or any server. Output files are root-owned; PUID/PGID from the README are ignored. The image pins vcpkg 2023.04.15, not the CI baseline, and builds only the legacy Sciter binary.

5.7 Building from source (native)

git clone --recurse-submodules https://github.com/rustdesk/rustdesk
cd rustdesk
export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus aom
python3 build.py --flutter --hwcodec --unix-file-copy-paste
(Debian/Ubuntu host: produces rustdesk-1.5.0.deb in the repository root)
Host build.py output
Windows + --flutter rustdesk-<ver>-install.exe (portable packer)
Windows Sciter rustdesk-<ver>-win7-install.exe
Debian/Ubuntu + --flutter rustdesk-<ver>.deb (rustdesk-unattended-wayland-<ver>.deb with --drm)
Arch (pacman) rustdesk-<ver>-manjaro-arch.pkg.tar.zst
Fedora (yum) / openSUSE (zypper) Sciter-style rustdesk-<ver>-fedora28-centos8.rpm / -suse.rpm (no Flutter path)
macOS + --flutter RustDesk.app (no dmg; dmg made by CI or res/osx-dist.sh)

Cargo features: flutter, hwcodec, vram (Windows), mediacodec (Android), drm, drm-wake, linux-pkg-config, unix-file-copy-paste, screencapturekit (macOS), inline (Sciter), resampler features use_dasp (default), use_samplerate, use_rubato. Release profile: lto = true, codegen-units = 1, panic = 'abort', strip = true.

The Flutter–Rust bridge is generated from src/flutter_ffi.rs:

cargo install flutter_rust_bridge_codegen --version 1.80.1 --features uuid --locked
flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs \
  --dart-output ./flutter/lib/generated_bridge.dart \
  --c-output ./flutter/macos/Runner/bridge_generated.h
(generates src/bridge_generated.rs, flutter/lib/generated_bridge.dart and the C header; all git-ignored)

5.8 CI/CD

Workflow Trigger Purpose
ci.yml PR, push to master, manual x86_64 Linux cargo build + cargo test --workspace (skips two cursor/key-state tests)
flutter-ci.yml PR, push, manual Full Flutter build without uploads
flutter-nightly.yml daily 00:00 UTC, manual Full build, publishes the nightly release
flutter-tag.yml tags vX.Y.Z / vX.Y.Z-N Full build, publishes the tag release
flutter-build.yml reusable SBOM (syft CycloneDX), bridge, Windows x64/arm64 + x86 Sciter, macOS x64/arm64 dmg, Android per-ABI + universal APK, Linux deb/rpm/Arch, unattended-Wayland deb, Sciter debs, AppImage, Flatpak; iOS lib only; web job disabled
bridge.yml reusable flutter_rust_bridge 1.80.1 codegen
third-party-RustDeskTempTopMostWindow.yml reusable Builds the privacy-mode topmost-window helper
fdroid.yml tags, manual Publishes rustdesk-version.txt (versionCode = X·10⁶+Y·10⁴+Z·10²+A)
update-webpki-roots.yml Mondays 03:00, manual PR bumping webpki-roots
clear-cache.yml, playground.yml manual Cache purge / experiments

Signing: Windows binaries are sent to a signing task server with res/job.py (BASE_URL, SECRET_KEY, SIGN_TIMEOUT, TIMEOUT), only when the SIGN_BASE_URL secret exists; macOS uses codesign + rcodesign notary-submit --staple; Android uses r0adkll/sign-android-release.


6. Security

6.1 Threat model summary

Asset Protection Residual risk
Session content Ed25519-authenticated X25519/secretbox E2E encryption, also through relays Silent downgrade to unencrypted if key verification fails (user prompt only); direct IP never E2E
Host access Password challenge-response, approve mode, 2FA, IP/ID whitelists, per-feature permissions In-memory lockout counters; 6-char default one-time password
Stored secrets Salted SHA-256 password hash; secretbox with machine-UUID key Obfuscation against local attackers who can read config and machine UUID
Metadata Self-hosted server option Public server sees IDs, public keys, IPs, NAT types
Policy Custom-client signing, server strategies Server strategy push can change any option

6.2 Authentication

Password verification. The host sends Hash{salt, challenge}; the client replies with SHA256(SHA256(password + salt) + challenge), compared in constant time. Temporary passwords are checked first (if enabled), then the permanent password (if enabled, or if built-in allow-logon-screen-password applies at a lock/logon screen).

Rule Value
Temporary password alphabet 23456789abcdefghijkmnpqrstuvwxyz (no 0 1 l o); digits only with allow-numeric-one-time-password
Temporary password length 6 / 8 / 10 (default 6)
Rotation After every successful authorization; after 10 consecutive wrong attempts
Permanent password storage "01" + encrypted(SHA256(password + salt)), 32-char salt; legacy plaintext accepted
Permanent password complexity Flutter UI only: digit + upper + lower, length ≥ 8 (CLI enforces none)
Max stored length 128 characters
Changing the permanent password Clears trusted devices
Recent-session reconnect Same session key within 30 s is re-authorized without prompting

Rate limiting (per process, in memory):

Bucket Threshold Message Audit alarm
Per IP, per minute > 6 "Please try 1 minute later" SixAttemptsWithinOneMinute
Per IP, total > 30 "Too many wrong attempts" ExceedThirtyAttempts
IPv6 /64, /56, /48 > 60, > 80, > 100 "Too many wrong attempts for IPv6 prefix /N" ExceedIPv6PrefixAttempts
Windows terminal OS login from 3rd failure: 15 s × 2ⁿ⁻³, max 30 min, reset after 120 min idle "Please try again in N second(s)." TerminalOsLoginBackoff

Two-factor authentication. TOTP (SHA1, 6 digits, 30 s step, skew 1, issuer "RustDesk Connection", account = device ID). The secret is stored encrypted in option 2fa. With a Telegram bot token (option bot), each 2FA-gated login sends the current code and the source IP to https://api.telegram.org/bot<token>/sendMessage. Trusted devices (hwid = SHA-256 of machine UID, plus ID, name and platform) skip 2FA for 90 days.

Account login (API). OIDC via /api/oidc/auth and polling /api/oidc/auth-query (1 s interval, 180 s max); username/password via /api/login. With "remember me", access_token and user_info are stored as local options.

6.3 Authorization and access control

Order of checks for an incoming session:

  1. Admission control (64 unauthenticated total, 16 per IP).
  2. IP whitelist → "Your IP is blocked by the peer" + alarm.
  3. allow-only-conn-window-open → "The main window is not open".
  4. Challenge sent; connection audit posted.
  5. Login type latched (a different type later → "Connection not allowed").
  6. Trusted-device check (may clear 2FA).
  7. ID whitelist → "Your ID is blocked by the peer" (failure-counted).
  8. Per-type permission (file, camera, terminal, tunnel).
  9. Privacy mode already active → refused.
  10. Username must equal this device's ID (or direct IP) → else "Offline".
  11. Approve mode / password.
  12. 2FA, then authorize.

Permission resolution. A server-pushed ControlPermissions bit pair (2 bits per permission: 1 = disable, 2 = enable) wins; otherwise the local enable-* option; on Flutter desktop access-mode=full forces everything on and view forces everything off. The Connection Manager can toggle keyboard, clipboard, audio, file, restart, recording, block-input and privacy-mode live.

Session scope. After authorization, messages outside the session type are logged; allow-scope-violation-alarm posts an alarm and allow-scope-violation-close closes the session.

Warning

allow-remote-config-modification is enforced only by the host's Flutter settings UI (it blocks the settings page while remote input is detected). It does not gate protocol-level actions. The API server's heartbeat strategy.config_options can set any option key, including whitelists, approve mode and permissions — protect the API server accordingly.

6.4 Encryption and key exchange

Stage Mechanism
Device identity Ed25519 key pair generated once, stored in RustDesk.toml
Client ↔ hbbs KeyExchange signed by the server key; kx-v2 signed params (rdkx-params domain); secretbox after exchange; tolerated absence on old hbbs unless required
Peer ↔ peer Host sends SignedId{id, ephemeral box pk, dtls_fingerprint, kx_version} signed by its Ed25519 key; the controller verifies it using the host key vouched for by hbbs, sends a box-sealed random secretbox key
Cipher XSalsa20-Poly1305 secretbox with per-direction counter nonces
kx v1 Per-direction subkeys = BLAKE2b keyed hash over rdkx-spl, direction, versions and both ephemeral keys
WebRTC DTLS; signed fingerprint must match or the controller aborts ("possible MITM") and falls back to relay
Fingerprint UI Hex of the peer identity key, grouped by 4 characters
Failure Behaviour
hbbs-supplied peer key missing/invalid Logged "Handshake failed: invalid public key from rendezvous server"; continues unencrypted
Peer signature mismatch "pk mismatch, fall back to non-secure"; host clears key_confirmed; unencrypted
Any of the above on WebRTC Hard failure, relay fallback
Unencrypted non-direct session Controller dialog "Insecure Connection" (conn-e2ee-unavailable-tip) — Continue or Close
Direct IP Always unencrypted, no prompt

Caution

Direct IP access (direct-server=Y) listens on all interfaces on port 21118, performs no signed-key handshake and carries no server-side permissions. Enable it only on trusted networks, combine it with an IP whitelist, and prefer ID-based connections through your own server.

6.5 TLS / certificates

  • HTTP API, wss:// and HTTPS proxies use rustls with bundled webpki-roots plus native OS roots, with a platform-verifier fallback; no revocation checking.
  • On failure, native-tls is tried; invalid certificates are accepted only with allow-insecure-tls-fallback=Y (per host:port, cached).
  • The *_strict HTTP client variants never fall back and refuse plain http:// URLs ("Strict HTTP client requires HTTPS").
  • allow-websocket=Y skips RustDesk's own rendezvous key exchange and relies on wss; with an http:// API server the WebSocket scheme is plain ws://.
  • webpki-roots is refreshed weekly by an automated PR workflow.

6.6 Secrets at rest

Item Storage
Permanent password Salted SHA-256, encrypted with secretbox keyed by the machine UUID (zero-padded to 32 bytes; device public key as fallback)
ID, socks password, unlock PIN, trusted devices, peer passwords, RDP/OS credentials "00" + base64(version byte + 24-byte nonce + secretbox ciphertext), max 128 chars
2FA secret, Telegram token Same "00" format
Identity secret key Config.key_pair in RustDesk.toml
RDP credentials on the controller Windows Credential Manager via cmdkey /generic:localhost

Anyone who can read the config files and obtain the machine UUID can decrypt these values; treat config directories as sensitive and restrict filesystem access.

6.7 Data sent to third parties by default

Destination Data Condition
rs-ny.rustdesk.com:21116 Device ID, machine UUID, identity public key, public IP/port, NAT type; per connection: target ID, NAT type, IPv6 address, WebRTC SDP Public server in use
rs-ny.rustdesk.com:21117 Relayed session bytes (E2E encrypted when secured) Relay used
https://api.rustdesk.com/version/latest OS, version, arch, device fingerprint Not a custom client and enable-check-update ≠ N
STUN servers (Cloudflare, Google, antisip, Nextcloud) STUN binding requests WebRTC/IPv6 discovery
<ipv4>.nip.io DNS Peer IPv4 in a DNS query Connecting from IPv6 to an IPv4 target
api.telegram.org 2FA code + source IP Only with a bot token

Heartbeat, sysinfo, audit and switch-grant uploads are disabled when the API host is *.rustdesk.com.

6.8 Hardening checklist

  • [ ] Deploy your own ID/relay/API server and distribute the key to every client
  • [ ] Set approve-mode and verification-method deliberately; avoid permanent passwords where attended support is enough
  • [ ] Enforce 2FA for unattended hosts; review trusted devices
  • [ ] Use whitelist (CIDR) for admin networks; keep direct-server off
  • [ ] Disable unneeded permissions (enable-tunnel, enable-terminal, enable-file-transfer, enable-camera)
  • [ ] Set enable-lan-discovery=N on hosts where the LAN is untrusted (discovery replies reveal ID, MAC, hostname and logged-in username)
  • [ ] Set an unlock PIN; ship a signed custom client with disable-settings/hide-* where users must not change policy
  • [ ] Keep allow-insecure-tls-fallback off; use https API servers
  • [ ] Protect the API server — its strategies can rewrite any client option
  • [ ] Restrict read access to config directories; do not copy them between machines

7. Operations

7.1 Start, stop and status

Platform Start / stop Status
Windows sc start RustDesk / sc stop RustDesk; rustdesk --install-service / --uninstall-service sc query RustDesk
Linux sudo systemctl start rustdesk / stop; rustdesk --install-service (privileged systemctl enable/start) systemctl status rustdesk
macOS In-app Install / Stop service; launchctl on com.carriez.RustDesk_service `launchctl list
All Tray Stop service (sets stop-service=Y) Status bar Ready
sudo systemctl restart rustdesk
rustdesk --get-id
123456789

Tip

--uninstall-service sets stop-service=Y, which persists: a later reboot will not start the service. Use --install-service (or Start service) to clear it.

7.2 Health checks

Check How Healthy
Registration Status bar or log lines about registration Ready
ID server reachability TCP/UDP 21116, TCP 21115 Open
Relay reachability TCP 21117 Open
Service running sc query / systemctl status / launchctl list Running
Local IPC rustdesk --get-id prints an ID ID printed
Hardware codec Log Check hwcodec config, exit with: ... exit status 0

7.3 Logging

OS Location
Windows %APPDATA%\RustDesk\log\<process> (service: C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\log)
Linux ~/.local/share/logs/RustDesk/<process>
macOS ~/Library/Logs/RustDesk/<process>; daemon stdout/stderr in /var/log/rustdesk_service.out / .err
Android <home>/RustDesk/Logs

Each process role logs to its own subfolder named after its first argument (server, service, tray, cm, portable-service; the main UI has none).

  • debug and above are written to file. The project's logging rule: anything that can fire per packet/frame/input must use trace or hbb_common::throttled_log!.
  • Logs contain peer IPs and ports, device IDs, relay UUIDs, rendezvous hosts, NAT type and OIDC request results. No log statement prints passwords, tokens or keys.
  • Heartbeat failures and Wayland portal errors are throttled (Wayland: repeat only when the cause changes or after 600 s).

Note

The session logs include source IPs ("Connection opened from <addr>", "direct access from <addr>"), so treat them as personal data under your retention policy.

7.4 Backup and restore

What Where Notes
Identity (ID + key pair + permanent password) RustDesk.toml Encrypted with the machine UUID — restores only on the same machine
Server config and security options RustDesk2.toml Same caveat for encrypted fields
Peer list and remembered passwords peers/*.toml Passwords encrypted per machine
UI preferences RustDesk_local.toml, RustDesk_default.toml Portable

Restore on the same machine:

sudo systemctl stop rustdesk
sudo cp -a backup/rustdesk/. /root/.config/rustdesk/
sudo systemctl start rustdesk
(no output; the status bar returns to Ready with the original ID)

Moving to new hardware: re-apply options with --config / --option or a custom client rather than copying encrypted files; set the ID with --set-id if you need to keep it (6–16 chars, letter first).

7.5 Upgrades

Platform Mechanism
Windows EXE install allow-auto-update=Y (installed service) or the home-screen Update card; --update for custom clients
Windows MSI New MSI (major upgrade); the updater re-creates the service with --import-config
macOS Silent auto-update from the service binary; rustdesk --update [file.dmg]; deferred while a session is active
Linux Package manager
Android APK / F-Droid

7.6 Capacity notes

  • One encoder per display is shared by all viewers; a new viewer restarts the encoder so everyone gets a key frame.
  • The slowest viewer gates the frame rate of a display (capture waits up to 3 s for all viewers to fetch a frame).
  • Terminal: max 100 terminal services per host, 1 MB scrollback buffer each; idle cleanup after 1 h (non-persistent) or 2 h (persistent with no terminals).
  • Port-forward mux: 256 channels per tunnel.

8. Performance Tuning

8.1 Quality, bitrate and FPS

Knob Scope Default Effect
image_quality controller, per peer balanced Ratio 1.5 (best) / 0.67 (balanced) / 0.5 (low) / custom×2/100
custom-fps controller 30 Server accepts 1–120; public server caps relayed sessions at 30
codec-preference controller auto Auto: H.265 > H.264 > AV1 (if av1-test ≠ N) > VP9; VP8 on ≤ 4 GB RAM; majority vote across viewers
i444 controller off 4:4:4 chroma for VP9/AV1 only; all viewers must want it
enable-abr host on Adjust ratio every 3 s; N pins the ratio
enable-hwcodec both on Hardware H.264/H.265
enable-directx-capture Windows host on DXGI vs GDI (GDI disables VRAM)
allow-d3d-render Windows viewer off Needed for zero-copy VRAM decode
allow-remove-wallpaper host off Fewer changed pixels

Bitrate table (kbps, VP8/VP9 base, nearest preset scaled by pixels): 1280×720 = 1000, 1920×1080 = 2073, 2560×1440 = 3000, 3840×2160 = 5000, 7680×4320 = 12000. Hardware encoders multiply by a factor (2.0 for H.264, 1.5 for H.265 below 2000 kbps, decaying toward 1.0).

8.2 Video QoS constants

Constant Value
Default / min / max FPS 30 / 1 / 120
New viewer start FPS 15 for the first second
Automatic floor 5 FPS
Delay threshold 150 ms above learned baseline
Congestion 3 bad replies → drop ≤ 1/5; ≥ 1000 ms excess halves at once
ABR interval 3 s; ×1.15 / ×1.1 / ×1.05 increases when the screen is dynamic
Ratio bounds 0.1 (high resolution) / 0.2 – 40
RTT baseline window 20 samples

8.3 Codec behaviour worth knowing

  • Periodic keyframes are disabled (VPX KF_DISABLED, hardware GOP = i32::MAX) except while recording (every 240 frames).
  • VAAPI encoders cannot change quality on the fly; quality changes restart the encoder.
  • MediaCodec and VideoToolbox are not "latency free": the last frame is re-encoded up to 10 times when idle.
  • The client marks a codec unsupported after 3 consecutive decode failures and renegotiates.
  • Encoder creation failure falls back to VP9; hardware failures clear the cached hwcodec config.

8.4 Platform tips

Symptom Tuning
High CPU on host Use hardware codec, lower FPS, choose Optimize reaction time
Blurry text True color (4:4:4) with VP9/AV1, or Good image quality
Stutter on slow links Keep ABR on; consider VP8 on weak hosts
Black screen with D3D Turn off allow-d3d-render / texture rendering
Linux NVIDIA window closes allow-always-software-render=Y + restart
High audio latency Default systemd env sets PULSE_LATENCY_MSEC=60, PIPEWIRE_LATENCY=1024/48000

Tip

Audio encoding stops automatically after 800 silent packets (about 8 s), and idle desktops send no new video frames with VPX/AOM/VRAM encoders, so idle sessions cost little bandwidth.


9. Integrations

9.1 Self-hosted server HTTP API (client side)

Method Path Purpose
POST /api/heartbeat Every 3 s tick (≥ 15 s when idle): {id, uuid, ver, conns?, modified_at}; response may request sysinfo, disconnect connection ids, or push strategy.config_options
POST /api/sysinfo / /api/sysinfo_ver CPU, memory, OS, hostname, username, version, preset address book/group/user/strategy/device fields
POST /api/audit/conn, /api/audit/file, /api/audit/alarm Connection, file-transfer and alarm audits (retried up to 120 s; server dedups by nonce)
POST /api/switch-grant Signed switch-sides grant
GET /api/login-options OIDC providers
POST /api/oidc/auth, GET /api/oidc/auth-query OIDC login
POST/GET /api/login, /api/logout, /api/currentUser, /api/users, /api/peers, /api/device-group/accessible, /api/ab* Account, address book and group calls from the Flutter UI
POST /api/devices/deploy rustdesk --deploy --token <t> [--id <id>]
POST /api/devices/cli rustdesk --assign --token <t> ...
POST /api/record Recording upload (code present, never enabled in 1.5.0)

With builtin use-raw-tcp-for-api, API calls are tunnelled as HttpProxyRequest over an encrypted TCP connection to hbbs port 21116 (36 s timeout).

Deploy exit codes:

Result Printed Exit
OK Device deployed. 0
NOT_ENABLED Server does not require deployment. 3
INVALID_INPUT Invalid input. 5
ID_TAKEN Id `<id>` is already used by another machine on the server. 6
other error text 1
sudo rustdesk --assign --token "$TOKEN" --device_group_name "Servers" --note "rack 4"
Done!

9.2 Server Pro management scripts (res/*.py)

All take a positional command plus --url <api base> and --token <bearer>; filters are fuzzy (%value%) unless - or already containing %; bulk operations on more than one match require typing Y.

Script Commands Endpoints
devices.py view, disable, enable, delete, assign (--assign_to type=value), filters --id --device_name --user_name --group_name --device_group_name --offline_days /api/devices, `/api/devices/{guid}/disable
users.py view, disable, enable, delete, new, invite, enable-2fa-enforce, disable-2fa-enforce, disable-email-verification, reset-2fa, force-logout /api/users, /api/users/invite, /api/users/tfa/totp/enforce, /api/users/disable_login_verification, /api/users/force-logout
user-groups.py view, add, update, delete, view-users, add-users /api/user-groups
device-groups.py view, add, update, delete, view-devices, add-devices, remove-devices /api/device-groups
strategies.py list, view, enable, disable, assign, unassign /api/strategies, /api/strategies/{guid}/status, /api/strategies/assign
control-roles.py view, add, update, delete, enable, disable, view-users, assign-users, remove-users /api/control-roles
admin-roles.py view, add, update, delete, view-users, add-users, remove-users /api/admin-roles
audits.py view-conn, view-file, view-alarm, view-console (--conn-type 0 Remote Desktop, 1 File Transfer, 2 Port Transfer, 3 View Camera, 4 Terminal) `/api/audits/{conn
ab.py address books, peers, tags, rules (`--rule-permission ro rw
python3 res/devices.py view --url https://rd.example.com --token "$TOKEN" --offline_days 30
(prints matching devices with last_online at least 30 days ago)

Warning

devices.py and users.py without filters act on every record the list call returns. Always run view with the same filters first.

9.3 Plugin / extension system

There is no runtime plugin loader in the 1.5.0 client. Extension points are:

Mechanism What you can change
Signed custom client (custom.txt) App name, default/override options, built-in and hard settings
Server strategies (heartbeat config_options) Any option on managed clients
ControlPermissions from the server Per-session permission overrides
ice-servers option Custom STUN/TURN servers for WebRTC
rustdesk:// URI scheme and CLI launchers Integrate with ticketing / RMM tools
Remote printer driver Windows printing to the controller

9.4 Other integrations

  • RDP: tunnels to localhost:3389 on the host and launches mstsc on the controller, credentials via cmdkey.
  • Wake-on-LAN: sent to MAC addresses from LAN discovery.
  • Telegram: 2FA code delivery.
  • Windows file clipboard: CLIPRDR (FileGroupDescriptorW / FileContents); Linux FUSE mounts cliprdr-client / cliprdr-server beside the IPC directory.

10. Developer Internals

10.1 Repository structure

Path Contents
src/ Rust application: entry (main.rs, core_main.rs), client (client.rs, client/), server (server.rs, server/), rendezvous, IPC, platform, privacy mode, FFI, legacy Sciter UI, languages (lang/)
src/server/ connection.rs (per-session state machine), video_service.rs, video_qos.rs, audio_service.rs, input_service.rs, clipboard_service.rs, display_service.rs, printer_service.rs, terminal_service.rs, terminal_helper.rs, portable_service.rs, uinput.rs, wayland.rs, rdp_input.rs, drm_capturer.rs, port_forward_mux.rs, login_failure_check.rs
src/hbbs_http/ sync.rs (heartbeat/sysinfo/strategy), account.rs (OIDC), http_client.rs, downloader.rs, record_upload.rs
src/platform/ windows.rs/windows.cc, macos.rs/macos.mm, linux.rs, gtk_sudo.rs, privileges_scripts/ (launchd plists, AppleScripts)
libs/hbb_common Submodule: config.rs, password_security.rs, config/permanent_password.rs, protobuf protos/rendezvous.proto and message.proto, tcp.rs, udp.rs, proxy.rs, tls.rs, verifier.rs, websocket.rs, webrtc.rs, socket_client.rs, log_throttle.rs
libs/base config/keys.rs (single import path for all option keys), fs.rs, keyboard.rs, platform/linux.rs, protos
libs/scrap Capture (dxgi, quartz, x11, wayland, android), codecs (common/vpxcodec.rs, aom.rs, hwcodec.rs, vram.rs, codec.rs, record.rs, camera.rs, drmtap_dl.rs)
libs/enigo, libs/clipboard, libs/virtual_display, libs/remote_printer, libs/portable, libs/libxdo-sys-stub Input, clipboard, IDD, printer, portable packer, libxdo stub
flutter/ Flutter app: lib/desktop, lib/mobile, lib/common, lib/models, platform runners, test/
res/ Packaging (DEBIAN, rpm specs, PKGBUILD, msi, systemd unit, desktop files), vcpkg overlays/triplets, helper scripts
appimage/, flatpak/, fastlane/ Other packaging and store metadata
.github/workflows/ CI/CD
AGENTS.md Contributor/agent rules (Rust, Tokio, logging, editing hygiene, localization)

10.2 Protocol

Two protobuf files in libs/hbb_common/protos define the wire protocol:

File Key messages
rendezvous.proto RegisterPeer, RegisterPk / RegisterPkResponse (OK, UUID_MISMATCH, ID_EXISTS, TOO_FREQUENT, INVALID_ID_FORMAT, NOT_SUPPORT, SERVER_ERROR, NOT_DEPLOYED), PunchHoleRequest / PunchHole / PunchHoleSent / PunchHoleResponse (ID_NOT_EXIST, OFFLINE, LICENSE_MISMATCH, LICENSE_OVERUSE), FetchLocalAddr / LocalAddr, RequestRelay / RelayResponse, TestNatRequest/Response, ConfigUpdate, KeyExchange, HttpProxyRequest, ControlPermissions, IdPk
message.proto SignedId, PublicKey, Hash, LoginRequest / LoginResponse, PeerInfo / DisplayInfo, VideoFrame, AudioFrame, MouseEvent, KeyEvent (KeyboardMode Legacy/Map/Translate/Auto), Clipboard / MultiClipboards, FileAction / FileResponse, Misc (close reasons, permissions, privacy mode, audio format…), OptionMessage, SupportedDecoding

Framing is length-prefixed (bytes_codec.rs); after the key exchange every frame is secretbox-sealed with a counter nonce.

10.3 Connection state machine (host)

accept socket -> admission control -> identity_handshake (SignedId / PublicKey)
   -> Connection::start
      -> on_open: IP whitelist, window-open check, send Hash{salt,challenge}, audit "new"
      -> LoginRequest: latch type, trusted device, ID whitelist, per-type permission,
         privacy check, username==id, approve mode / password, 2FA
      -> authorize: rotate temp password, subscribe services (monitorN, audio, clipboard...)
      -> session loop: input, clipboard, file jobs, options, permission toggles,
         scope enforcement, idle / auto-disconnect timers
      -> close: lock-after-session-end, release keys, restore resolution, audit

10.4 Video pipeline

capturer (DXGI | GDI | Magnifier | Quartz | X11 SHM | PipeWire | DRM | Android)
   -> BGRA/texture frame -> (HDR->SDR on Windows) -> YUV (I420/NV12/I444)
   -> encoder (VPX | AOM | hwcodec | VRAM | MediaCodec), ratio from video_qos
   -> send_video_frame to subscribed connections -> wait for fetch (<= 3 s)
   -> sleep remainder of spf; recorder taps frames if recording

Windows fallbacks: DXGI empty → GDI; >3 no-image tries → GDI; ACCESS_LOST restarts up to 3 times per 10 s before GDI; other DXGI errors → GDI.

10.5 Keyboard modes

Mode Client sends Host injects
Map Physical scan code position Raw key by position (rdev; Wayland: evdev code + 8)
Translate Characters from the local layout; dead keys skipped; numpad falls back to map translate_keyboard_mode
Legacy Control keys + characters with modifiers enigo legacy_keyboard_mode

Support: Legacy always; Map needs peer ≥ 1.2.0 and not Android; Translate/Auto need peer ≥ 1.2.0. Unsupported saved modes fall back to the first supported (Legacy). Windows Pause is always sent as Legacy.

10.6 Code patterns and conventions (from AGENTS.md)

  • No unwrap()/expect() in production code except tests and lock poisoning; prefer Result + ?.
  • Tokio: never nest runtimes or block_on in async code; no locks held across .await; spawn_blocking for blocking work.
  • Logging: per-event sites use log::trace! or hbb_common::throttled_log!(interval, level, ...).
  • Imports: one braced use per crate, split only for differing #[cfg].
  • Minimal, additive diffs: new logic in feature modules; shared files (src/core_main.rs, src/server/connection.rs, src/tray.rs) get one-line hooks.
  • Client-only code belongs in libs/base; hbb_common changes require a server round-trip.
  • Localization: src/lang/template.rs is the master key list (never edit for translations); en.rs only overrides keys whose display text differs; it.rs is hand-maintained; new keys use sentence case and are appended to every language file.
  • Commits require a DCO sign-off (git commit -s).

10.7 Module reference (selected functions)

Module Function Purpose
src/core_main.rs core_main() Parses argv, dispatches on args[0], returns args for the UI or None to exit
src/rendezvous_mediator.rs RendezvousMediator::start_all() NAT test, sync, direct server, LAN listener, one registration task per server
src/client.rs Client::_start() / connect() / secure_connection() Punch/relay race and E2E handshake
src/client.rs handle_login_error(), check_if_retry() Maps login errors to dialogs; retry policy
src/server.rs create_tcp_connection(), new() Admission + handshake; service registry
src/server/connection.rs Connection::start(), validate_password(), check_failure() Session lifecycle, auth, rate limiting
src/server/video_service.rs run() Capture/encode loop
src/server/video_qos.rs VideoQoS FPS/ratio adaptation
libs/scrap/src/common/codec.rs Encoder::update() Codec negotiation
libs/scrap/src/common/hwcodec.rs check_available_hwcodec() HW probe
src/hbbs_http/sync.rs start() Heartbeat, sysinfo, strategy application
src/custom_server.rs get_custom_server_from_string() Exe-name / --config parser
src/common.rs read_custom_client(), get_api_server(), get_key() Custom client, API URL, server key
libs/hbb_common/src/config.rs Config::get_rendezvous_server(), option2bool() Server selection, boolean convention
libs/base/src/fs.rs TransferJob File transfer engine
src/virtual_display_manager.rs plug_in_headless() Virtual displays

10.8 Testing Strategy

Location Kind Run
Inline #[cfg(test)] modules (~745 tests in ~102 files across src/ and libs/) Unit tests cargo test --workspace
src/server/video_qos/tests/*.rs (adaptation, baseline, invariants, jitter, recovery, robustness, sim, smoke, startup) QoS simulations Part of cargo test
src/client/tests/audio_state_tests.rs, audio_playback_recovery_tests.rs; src/audio_resampler/*tests.rs; src/server/audio_service/*_tests.rs Audio Part of cargo test
src/core_main.rs tests CLI routing Part of cargo test
flutter/test/*.dart (14 files: cursor, file model, input modifiers, quality monitor, server settings dialog, terminal…) Flutter cd flutter && flutter test
libs/scrap/examples, examples/ipc.rs Runnable examples cargo run --example <name>
cargo test --locked --workspace --no-fail-fast -- --skip test_get_cursor_pos --skip test_get_key_state
test result: ok. ... passed; 0 failed; ...

CI runs unit tests only on x86_64 Linux (default features), three cursor Flutter tests on macOS arm64 PR builds, and a DRM-feature subset in the unattended-Wayland job. cargo fmt, clippy and MSRV jobs are commented out. Project policy: a fix gets 1–3 regression tests that fail on master and pass with the fix; no new test infrastructure without maintainer approval.

Note

The libs/hbb_common submodule must be checked out (git submodule update --init --recursive); the workspace does not load without it.


11. Troubleshooting

Operator-focused items. End-user dialogs (wrong password, portal prompts, macOS permission cards) are covered in the User Guide.

Start with the log of the right process role: problems accepting sessions are in the server log, service start/stop in service, approval UI in cm, and outgoing sessions in the main UI log (no subfolder).

Tip

Many connection problems are transport-specific. Before deep debugging, retry with <id>/r (forced relay): if the relayed session works, focus on NAT, UDP and firewall paths rather than authentication or capture.

11.1 Service and process

Error: ipc is occupied by another process, try kill it

Cause: A second --server could not bind the main IPC endpoint.

Solution:

  1. Stop the service, kill stray rustdesk --server processes, start the service again.
  2. On Linux check /tmp/RustDesk-<uid>/ for stale sockets owned by another user.

Prevention: Do not run the Flutter main UI and a manually started --server for the same user.

Error: service stays stopped after reboot

Cause: stop-service=Y is persisted (tray Stop service, --uninstall-service, Linux service honours it at start and disables itself).

Solution: rustdesk --install-service, Start service, or rustdesk --option stop-service "" as admin.

Prevention: Hide the stop button with built-in hide-stop-service on managed fleets.

Error: Installation and administrative privileges required!

Cause: A management command (--password, --set-id, --config, --option, --assign, --deploy, --set-unlock-pin) was run on a portable copy or without elevation.

Solution: Run from an elevated prompt / sudo against the installed binary.

Prevention: Script management commands after installation.

Error: Settings are disabled!

Cause: Hard setting disable-settings=Y (custom client).

Solution: Use a build with built-in allow-command-line-settings-when-settings-disabled=Y, or change policy through the server strategy.

Prevention: Decide CLI manageability when building the custom client.

Error: --config does nothing

Cause: --config is silent on success and on parse failure; the string may be malformed.

Solution: Read back with rustdesk --option custom-rendezvous-server and --option key.

Prevention: Generate strings with the UI Export Server Config, or use host=...,key=....

Error: Failed to set Ctrl-C handler: <e>

Cause: Linux --service could not install its signal handler.

Solution: Check for unusual process supervision; restart the unit.

Prevention: Run under the shipped systemd unit.

Error: Failed to write plists: <e>

Cause: macOS service --write-plists could not write the launchd files.

Solution: Run with administrator rights; check /Library/LaunchDaemons permissions.

Prevention: Use the in-app Install flow.

Error: daemon.plist not found in embedded resources

Cause: Broken macOS app bundle.

Solution: Reinstall the app from the dmg.

Prevention: Do not modify the bundle contents.

Error: Unknown app directory of current exe file

Cause: macOS app launched from an unexpected location.

Solution: Move RustDesk into /Applications.

Prevention: Deploy to /Applications only.

11.2 Registration and server

Error: Server requires deployment. Run rustdesk --deploy --token <api_token> on this device.

Cause: The server replied NOT_DEPLOYED; registration is throttled to once per 30 s.

Solution:

sudo rustdesk --deploy --token "$API_TOKEN"
Device deployed.

Prevention: Include --deploy in your install script for servers that require deployment.

Error: Id <id> is already used by another machine on the server. (exit 6)

Cause: --deploy --id requested an ID owned by another device.

Solution: Choose another ID or free it on the server.

Prevention: Plan ID naming.

Error: Cannot deploy an unregistrable device! / Cannot deploy Outgoing-only clients.

Cause: Built-in register-device=N or hard setting conn-type=outgoing.

Solution: Use a different client build for devices that must register.

Prevention: Match custom-client type to device role.

Error: Rendezvous connection is timeout

Cause: No message from hbbs for 1.5 × keep-alive on a TCP/WebSocket mediator.

Solution: Check the path to hbbs. Behind nginx WebSocket proxying, set proxy_read_timeout above 60 s.

Prevention: Monitor hbbs and any reverse proxy.

Error: Handshake failed: invalid public key from rendezvous server

Cause: The configured key is not a valid 32-byte base64 Ed25519 key, or the hbbs-supplied peer key did not verify.

Solution: Correct the key option; sessions otherwise downgrade to unencrypted.

Prevention: Distribute keys via export/import or custom clients.

Error: Signature mismatch in key exchange

Cause: The hbbs key exchange signature does not verify against the configured key (wrong key or impostor server).

Solution: Verify the server's public key file and the client key.

Prevention: Pin the key in a signed custom client.

Error: the rendezvous server did not complete the key exchange

Cause: A WebRTC offer required a secured rendezvous channel but hbbs is too old.

Solution: Upgrade hbbs, or disable WebRTC (enable-webrtc=N).

Prevention: Keep server and clients in step.

Error: ID changed unexpectedly

Cause: hbbs answered UUID_MISMATCH; the client regenerates its ID (Config::update_id()).

Solution: Communicate the new ID or restore with --set-id.

Prevention: Avoid cloning machines with the same config; each install must have its own machine UUID.

Error: too many unauthenticated connections from <ip>

Cause: Admission control (16 per IP, 64 total) during a burst or scan.

Solution: Investigate the source IP; legitimate clients retry.

Prevention: Use IP whitelists on exposed hosts; keep direct IP access off.

11.3 Capture and codecs

Warning

On Wayland, RustDesk deliberately does not reset a capturer after repeated "no frame" results, because resetting would re-trigger the portal consent dialog every few minutes. A frozen Wayland picture therefore needs a manual reconnect or a service restart.

Error: dxgi error, fall back to gdi / No image, fall back to gdi

Cause: DXGI Desktop Duplication failed or produced no frames.

Solution: Usually self-healing; GDI is slower and disables VRAM encoding. Update GPU drivers; check enable-directx-capture.

Prevention: Keep GPU drivers current.

Error: dxgi access lost after 3 restarts in 10 seconds, fall back to gdi

Cause: Repeated ACCESS_LOST (mode changes, secure desktop, driver resets).

Solution: Restart the session; investigate display mode churn.

Prevention: Avoid frequent resolution changes during sessions.

Error: MIT-SHM extension not working properly on select X11 server

Cause: The X server's SHM extension is broken (common in some VNC/Xvfb setups).

Solution: Use an X server with working MIT-SHM.

Prevention: Test capture on headless X setups before rollout.

Error: Fatal pipewire error (process exits)

Cause: A PipeWire capture error starting with "Did not receive a reply".

Solution: Restart PipeWire / the desktop portal; the service restarts --server.

Prevention: Keep PipeWire and portals updated.

Error: drm capturer for display N unavailable (...); falling back to PipeWire

Cause: DRM/KMS capture could not serve a display (unattended-Wayland build).

Solution: Check /usr/lib/rustdesk/libdrmtap.so.0 is present; review libdrmtap dlopen failed logs.

Prevention: Install the complete rustdesk-unattended-wayland package.

Error: Failed to create encoder: <err>, fallback to VP9

Cause: The negotiated encoder could not be created.

Solution: Automatic; if hardware, check drivers or disable enable-hwcodec.

Prevention: Keep the hwcodec probe results fresh (they reset when the GPU signature changes).

Error: new hw encoder failed: ..., clear config

Cause: A cached hardware encoder failed at runtime.

Solution: The cache is cleared and VP9 is used; the next probe re-evaluates.

Prevention: Disable hardware codec on GPUs with known issues (#16324, #16156).

Error: Please change codec and try again.

Cause: A screenshot was requested from a VRAM (texture) frame repeatedly.

Solution: Switch the session codec to VP9/AV1 and retry.

Prevention: Use software codecs when screenshots matter.

Error: HDR output but the SDR white level cannot be read, assuming 80 nits until it can

Cause: Windows HDR desktop on a build older than 1709 or an API failure.

Solution: None required; colors may be dim.

Prevention: Keep Windows updated.

11.4 Audio

Error: Invalid audio capture layout: sample_rate=<r>, channels=<c>

Cause: The capture device reports an unusable layout.

Solution: Choose another Audio Input Device (audio-input).

Prevention: Standardize audio devices on hosts.

Error: Failed to read audio data:<err> (Linux)

Cause: The PulseAudio _pa IPC (hosted by the CM process) failed.

Solution: Ensure PulseAudio/PipeWire-Pulse is running for the user; per code comment, reinstalling pulseaudio may help.

Prevention: Keep a user session with audio running.

Error: no system audio from Windows 7 hosts

Cause: WASAPI loopback is event-driven and does not work before Windows 10 1703.

Solution: Select a microphone/virtual input device instead.

Prevention: Upgrade hosts.

11.5 Input

Error: Rejected connection on protected uinput ipc channel due to executable mismatch

Cause: A process that is not the same RustDesk binary tried to use the uinput IPC.

Solution: Investigate the caller; after an upgrade, restart the service so all processes run the new binary.

Prevention: Restart the service after package upgrades.

Error: X11 input does nothing

Cause: libxdo could not be loaded at runtime; the stub turns input into no-ops.

Solution: Install libxdo3/libxdo4 (xdotool on Arch/SUSE).

Prevention: Install from distribution packages that declare the dependency.

Error: Block input has no effect

Cause: block_input is a no-op on Linux and macOS.

Solution: Use privacy mode on macOS; there is no equivalent on Linux.

Prevention: Document platform limits for your support staff.

11.6 Windows installation and elevation

Error: Application name must match [a-zA-Z0-9-]+

Cause: Custom client app name contains invalid characters.

Solution: Rebuild the custom client with a valid name.

Prevention: Validate names during branding.

Error: <app> is not installed.

Cause: Update requested with no installed copy.

Solution: Install first.

Prevention: Check install state in deployment scripts.

Error: Failed to run the update msi: <e> / Unsupported update file format

Cause: Updater could not launch the MSI or got an unexpected file.

Solution: Update manually with the new MSI.

Prevention: Allow msiexec and the download host.

Error: Failed to run portable service process

Cause: The elevated/SYSTEM helper for quick support could not start (also no display available!, already running).

Solution: Install RustDesk instead of using portable elevation.

Prevention: Install on managed machines.

Error: Failed to connect to connection manager

Cause: The server could not reach the _cm IPC after launching the CM (10 retries).

Solution: Restart RustDesk in the user session; check that a desktop session exists.

Prevention: Keep users logged in for attended approval flows.

Error: Win10 or greater required!

Cause: Remote printer install on older Windows.

Solution: Upgrade the host.

Prevention: Check OS version before enabling printing.

11.7 Virtual display and privacy mode

Error: There are already 4 monitors plugged in.

Cause: Amyuni IDD limit reached.

Solution: Plug out a virtual display.

Prevention: Use Plug out all at session end.

Error: Plugging in too frequently.

Cause: Headless plug-in is rate-limited to once per 3 s.

Solution: Wait and retry.

Prevention: None.

Error: The virtual display is managed by other processes.

Cause: Another process controls the Amyuni driver.

Solution: Close the other process or reboot.

Prevention: Avoid multiple virtual display tools.

Error: Failed to install driver.

Cause: The Amyuni IDD driver (usbmmidd_v2\usbmmIdd.inf) could not be installed.

Solution: Run as administrator; check driver signing policy.

Prevention: Pre-approve the driver in enterprise policy.

Error: Privacy occupied by another one.

Cause: Another connection owns privacy mode.

Solution: Wait for it to be released.

Prevention: Coordinate admins.

Error: No privacy window created / No privacy monitor found

Cause: Topmost-window privacy mode failed (missing WindowInjection.dll, broker failure).

Solution: Switch to "Mode 2" (virtual display) on installed hosts.

Prevention: Keep installation files intact.

11.8 Terminal and tunnels

Error: Maximum number of terminal services (100) reached

Cause: Terminal service limit per host.

Solution: Close persistent terminals; idle ones expire after 1–2 h.

Prevention: Avoid leaving many persistent terminals.

Error: Terminal service user mismatch detected.

Cause: A persistent terminal service ID was reused by a different user/token.

Solution: Open a new terminal.

Prevention: Do not share terminal service IDs.

Error: Failed to get user token. / Failed to get current session id.

Cause: The Windows service could not obtain the console user's token.

Solution: Log a user on, or use administrator credentials.

Prevention: Keep a session for terminal-heavy workflows.

Error: Port forward target not authorized

Cause: A multiplexed tunnel channel tried to open a target different from the login target.

Solution: Create a separate mapping per target.

Prevention: One mapping per host:port.

11.9 API and account

Error: Heartbeat failed: ...

Cause: The API server is unreachable or rejects heartbeats (throttled log).

Solution: Check api-server, TLS, and proxy settings.

Prevention: Monitor the API endpoint.

Error: Strict HTTP client requires HTTPS: <url>

Cause: A strict call targeted an http:// URL.

Solution: Use an https:// API server.

Prevention: Terminate TLS on the API server.

Error: No authed oidc is found (repeating)

Cause: OIDC login pending; the client polls for up to 180 s.

Solution: Complete login in the browser.

Prevention: None.

Error: Downloader not found / Failed to get content length

Cause: Update download failures.

Solution: Retry or update manually.

Prevention: Allow the release host.

11.10 Build

Error: Couldn't find VCPKG_ROOT, also can't fallback to homebrew because it's only for macos aarch64.

Cause: VCPKG_ROOT not set.

Solution: Install vcpkg dependencies and export VCPKG_ROOT.

Prevention: Use the CI-pinned vcpkg commit.

Error: cargo build failed, please check rust source code.

Cause: Windows Flutter build: target/release/librustdesk.dll missing.

Solution: Fix the cargo error above it; run flutter clean if needed.

Prevention: Build with the pinned Rust toolchain.

Error: --drm is only supported on the deb packaging path

Cause: --drm on pacman/yum/zypper hosts or non-Linux.

Solution: Build the DRM variant on Debian/Ubuntu (#16220 tracks Flatpak/RPM).

Prevention: Use the deb path for unattended-Wayland.

Error: libdrmtap packaging gate fails (CPU-only stub / helper fallback)

Cause: libdrmtap built without EGL or with the privileged helper.

Solution: Install libegl-dev libgles2-mesa-dev (Arch: mesa libglvnd), delete third_party/libdrmtap/build-pkg, rebuild with -Dhelper=disabled.

Prevention: Build libdrmtap via build.py --drm.

Error: Sciter build crashes with Rust ≥ 1.78

Cause: i128 ABI change breaks Sciter.

Solution: Use Rust 1.75 (CI pin).

Prevention: Pin the toolchain.

Cause: Bionic exports it only from API 24; jniLibs target API 21.

Solution: Handled by build.rs compiling android_ifaddrs.c; ensure VCPKG_ROOT is set (build.rs panics otherwise).

Prevention: Build via flutter/ndk_*.sh.

Error: Unrecognized feature from build.py

Cause: -f/--feature has no available features in 1.5.0.

Solution: Omit -f.

Prevention: None.


12. Potential Improvements

Area Observation Suggestion
Encryption downgrade Non-WebRTC key failures continue unencrypted with a dismissable prompt; direct IP is never E2E Offer a policy option to fail closed; add E2E for direct IP
Lockouts Failed-login counters are in memory and reset on restart Persist counters or add configurable thresholds
Secrets at rest Encryption key is the machine UUID Use OS keystores (DPAPI, Keychain, libsecret) where available
Strategy push Server config_options can set any option Allow-list keys or require signed strategies
Complexity rules Password rules exist only in the Flutter UI Enforce the same rules in the core / CLI
CLI UX No --help; --config and --option set are silent; flags must be first Add help text and success/failure output
Docker Build image ignores PUID/PGID, runs as root, pins old vcpkg, builds only Sciter Align with CI and README
build.py --portable and -f/--feature have no effect Remove or implement
Licence metadata RPM specs say GPL-3.0; LICENCE is AGPL-3.0 Correct spec files
Packaging deb uses /usr/lib/systemd/system, rpm/Arch /etc/systemd/system; purge removes only root config Unify paths; document user-config cleanup
Recording upload /api/record uploader exists but is never enabled Wire an option or remove dead code
Tests CI unit tests only on x86_64 Linux; fmt/clippy/MSRV jobs commented out; Flutter tests mostly not run Add Windows/macOS test jobs and full flutter test
Wayland One capturer, no follow-window, consent persistence only in ScreenCast mode Continue DRM path; broaden persistence
Android audio tip Text says Android 10, code requires SDK 30 (Android 11) Fix the translation key
Multi-server Comments state multiple rendezvous servers are "not used any more" Remove legacy multi-server code paths

13. Quick Reference

13.1 Paths

Item Windows Linux macOS
Binary C:\Program Files\RustDesk\rustdesk.exe /usr/bin/rustdesk → /usr/share/rustdesk/rustdesk /Applications/RustDesk.app
Config %APPDATA%\RustDesk\config (service: C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config) ~/.config/rustdesk (root: /root/.config/rustdesk) ~/Library/Preferences/com.carriez.RustDesk
Logs %APPDATA%\RustDesk\log ~/.local/share/logs/RustDesk ~/Library/Logs/RustDesk, /var/log/rustdesk_service.*
Service RustDesk ("RustDesk Service") rustdesk.service com.carriez.RustDesk_service / _server
IPC \\.\pipe\RustDesk\query* /tmp/RustDesk-<uid>/ipc* /tmp/RustDesk-<uid>/ipc*
Recordings service: %SystemDrive%\ProgramData\RustDesk\recording; user: <Videos>\RustDesk <Videos>/RustDesk <Videos>/RustDesk (user video folder)

13.2 Commands

rustdesk --version
rustdesk --get-id
sudo rustdesk --password 'S3cure-Pass-2026'
sudo rustdesk --set-id office-pc-01
sudo rustdesk --option custom-rendezvous-server rd.example.com
sudo rustdesk --option key <base64-key>
sudo rustdesk --config "host=rd.example.com,key=<base64-key>"
sudo rustdesk --deploy --token "$API_TOKEN"
sudo rustdesk --assign --token "$API_TOKEN" --user_name alice
rustdesk --install-service
rustdesk --uninstall-service
rustdesk --connect 123456789 --password abc123
1.5.0
123456789
Done!
Done!
(silent)
(silent)
(silent)
Device deployed.
Done!

13.3 Ports

Port Proto Service
21114 TCP API (default derived)
21115 TCP NAT test
21116 TCP/UDP ID/rendezvous
21117 TCP Relay
21118 TCP WS rendezvous; direct IP access
21119 TCP/UDP WS relay; LAN discovery (UDP broadcast)

13.4 Most-used option keys

Key Typical value
custom-rendezvous-server rd.example.com
relay-server rd.example.com
api-server https://rd.example.com
key server public key
approve-mode password
verification-method use-permanent-password
whitelist 10.0.0.0/8,192.168.1.0/24
enable-lan-discovery N
enable-tunnel / enable-terminal N where not needed
allow-auto-disconnect / auto-disconnect-timeout Y / 15
enable-hwcodec N on problematic GPUs
stop-service empty (running)