Configuration Reference
Configuration file location: ~/.config/lamco-rdp-server/config.toml
Flatpak: ~/.var/app/io.lamco.rdp-server/config/lamco-rdp-server/config.toml. The package ships a full, annotated example-config.toml alongside this reference.
[server]
Server connection settings.
| Key | Type | Default | Description |
|---|---|---|---|
| listen_addr | string | "[::]:3389" | IP:PORT to bind, and authoritative. "[::]:3389" is dual-stack (IPv4 + IPv6); use "0.0.0.0:3389" to force IPv4-only. --listen, --port, and LAMCO_RDP_PORT override it only when explicitly given |
| max_connections | integer | 10 | Maximum concurrent connections (0 = unlimited) |
| session_timeout | integer | 0 | Session timeout in seconds (0 = none) |
| use_portals | boolean | true | Use XDG Desktop Portal capture/input where available |
# IP:PORT. Use "[::]:3389" for dual-stack (IPv4 + IPv6); "0.0.0.0:3389" is IPv4-only.
listen_addr = "[::]:3389"
max_connections = 10 # 0 = unlimited
session_timeout = 0 # seconds, 0 = none
use_portals = true
# view_only = false
Unified transport: [server.transports] (optional)
A single accept layer takes TCP, Unix socket, AF_VSOCK, and WebSocket connections. AF_VSOCK enables Hyper-V Enhanced Session Mode (vsock Cargo feature); WebSocket and RDCleanPath serve browser and WASM clients (websocket feature, experimental in 1.4.4). When this table is absent, the server binds TCP on server.listen_addr.
[security]
Security and authentication settings.
| Key | Type | Default | Description |
|---|---|---|---|
| cert_path | string | "/etc/lamco-rdp-server/cert.pem" | TLS certificate path. Self-heals: regenerated at startup if absent |
| key_path | string | "/etc/lamco-rdp-server/key.pem" | TLS private key path |
| enable_nla | boolean | false | Network Level Authentication. Requires auth_method = "pam" |
| auth_method | string | "none" | "none" = no auth (trusted networks or Flatpak); "pam" = system accounts (native installs only) |
| require_tls_13 | boolean | false | Reject clients that cannot negotiate TLS 1.3 |
| security_mode | string | "tls" | tls, or rdp (alias none) for plaintext Standard RDP Security. Plaintext serves Hyper-V Enhanced Session Mode over vsock only, is opt-in, and is never auto-selected |
cert_path = "/etc/lamco-rdp-server/cert.pem" # self-heals if absent
key_path = "/etc/lamco-rdp-server/key.pem"
enable_nla = false # NLA requires auth_method = "pam"
# "none" = no auth (trusted networks / Flatpak); "pam" = system accounts (native only)
auth_method = "none"
require_tls_13 = false
No IP allowlist setting. There is no allowed_ips or CIDR filtering in the server. Restrict access by source IP at the host firewall instead.
[video]
Video encoding settings.
| Key | Type | Default | Description |
|---|---|---|---|
| codec | string | "avc420" | Video codec (avc420, avc444) |
| encoder | string | "auto" | Encoder (auto, software, vaapi, nvenc) |
| quality | integer | 80 | Quality 1-100 |
| max_fps | integer | 60 | Maximum frame rate |
[audio]
Audio streaming settings (v1.2.0+).
| Key | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | true | Enable audio streaming |
| codec | string | "opus" | Audio codec |
| sample_rate | integer | 48000 | Sample rate in Hz |
| frame_ms | integer | 20 | Capture chunk size (milliseconds). Capture is real from 1.4.5, and the whole path runs at the client's rate with 44.1 kHz offered first |
| opus_bitrate | integer | 64000 | Opus bitrate in bits per second |
enabled = true
codec = "auto" # auto | opus | pcm | adpcm
sample_rate = 48000
channels = 2
frame_ms = 20
opus_bitrate = 64000
[egfx]
Extended Graphics Framework (EGFX) pipeline settings for RDP 8+ clients.
| Key | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | true | Enable EGFX pipeline |
| codec | string | "auto" | EGFX codec (auto, avc420, avc444) |
| aux_omission | boolean | true | Omit auxiliary data for bandwidth savings |
| frame_ack_timeout | integer | 5000 | Milliseconds. Enforced from 1.4.5: once the oldest unacknowledged frame exceeds it, outstanding frames are dropped and an IDR is forced |
| max_frames_in_flight | integer | 3 | Unacknowledged frames allowed before the encoder is throttled |
enabled = true
codec = "auto"
aux_omission = true
[hardware_encoding]
GPU encoding for the EGFX H.264 pipeline. From 1.4.5 this setting is actually used: earlier releases exposed the toggle but the display pipeline never called the hardware encoder.
| Key | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | false | Use VA-API for EGFX H.264 (AVC444 and AVC420), falling back to software OpenH264 when no GPU encoder can be built. Only VA-API is used for EGFX today; NVENC and Vulkan Video builds fall back to software |
enabled = false
Requires a VA driver with an H.264 encode entrypoint and the vainfo tool. The choice is made when the encoder is constructed, so a failure falls back before the first frame rather than mid-stream, and the log names the path taken.
[display]
Wayland color and output observers, dynamic resolution, and tone-mapping. The observers are read-side only: the server captures through PipeWire and owns no surface.
| Key | Type | Default | Description |
|---|---|---|---|
| color_management | boolean | true | wp_color_management_v1 observer, where the compositor advertises it |
| output_management | boolean | true | zwlr_output_management_v1 observer (wlroots family) |
| resize_drives_output_mode | boolean | true | A client-initiated resize switches the compositor output mode on wlroots. Best-effort; the first enabled head is the target. Requires output_management |
| hdr_tone_mapping | boolean | false | Experimental PQ/HLG to SDR tone-map before encode. Off by default because many compositors already tone-map to SDR themselves. Requires color_management |
| frame_transform | string | "auto" | Renamed from allow_rotation, which is now silently ignored |
color_management = true
output_management = true
resize_drives_output_mode = true
hdr_tone_mapping = false # experimental
frame_transform = "auto" # renamed from allow_rotation
[capture]
New in 1.4.4. Settings for the portal-generic (direct Wayland) capture strategy.
| Key | Type | Default | Description |
|---|---|---|---|
| protocol | string | "auto" | auto, ext, or wlr |
| allow_fallback | boolean | true | Fall back to wlr-screencopy if the preferred protocol does not complete |
| handshake_timeout_ms | integer | 5000 | How long to wait for a capture protocol handshake before falling back |
| gnome_record_mode | string | "auto" | GNOME only. auto uses area capture on a single monitor, which works around the fullscreen-video freeze (mutter#3903); area prefers area with the same fallbacks; monitor always uses the monitor stream, accepting the freeze |
| gnome_virtual_is_platform | boolean | false | Headless GNOME only. Presents the virtual monitor as a real display (GNOME 46 and newer) |
Some compositors advertise ext-image-copy-capture-v1 but never complete the handshake (Hyprland 0.54 and later). The server waits handshake_timeout_ms, then falls back to wlr-screencopy.
[monitoring]
New in 1.4.4. Optional Prometheus /metrics and JSON /health HTTP endpoints. Active only when the server is built with the metrics-server Cargo feature.
| Key | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | false | Enable the HTTP metrics server |
| snapshot_interval_secs | integer | 5 | How often health sensors are snapshotted |
| metrics_bind | string | "127.0.0.1:9100" | Bind address for /metrics and /health |
[performance]
Performance tuning and frame delivery settings. Also see [performance.adaptive_fps] and [performance.latency], and [hardware_encoding] for VA-API, NVENC, and the Vulkan Video backend.
| Key | Type | Default | Description |
|---|---|---|---|
| latency_mode | string | "balanced" | Latency governor mode (low, balanced, quality) |
| min_fps | integer | 5 | Minimum frame rate floor |
| max_fps | integer | 60 | Maximum frame rate ceiling |
| damage_tracking | boolean | true | Only encode changed screen regions |
latency_mode = "balanced"
min_fps = 5
max_fps = 60
damage_tracking = true
[damage_tracking]
When a compositor over-reports how much of the frame changed, the server stops trusting its damage hints and uses its own pixel-diff detector for that connection.
| Key | Type | Default | Description |
|---|---|---|---|
| compositor_hint_distrust_threshold_pp | integer | 15 | Percentage points of divergence between the compositor's hint and the server's own measurement |
| compositor_hint_distrust_consecutive_samples | integer | 3 | Consecutive diverging samples before the server distrusts the hints for the rest of the connection |
compositor_hint_distrust_threshold_pp = 15
compositor_hint_distrust_consecutive_samples = 3
The decision is sticky per connection and each new connection starts trusted, so a compositor fixed upstream regains the fast path automatically. An informational CompositorDamageHintsDistrusted health event marks it.
[input]
Keyboard, mouse, and touch input settings.
| Key | Type | Default | Description |
|---|---|---|---|
| keyboard_layout | string | "auto" | The client's announced layout is applied for US, UK, German, French, Belgian, Italian, Spanish, and Portuguese. Anything else keeps US scancodes |
| mouse_acceleration | boolean | false | Enable mouse acceleration |
| input_protocol | string | "auto" | Renamed from use_libei, which is now silently ignored. auto, libei, portal, or wlr |
| enable_touch | boolean | false | MS-RDPEI multitouch on the libei path. When off, the RDPEI channel is not offered to the client at all |
input_protocol = "auto" # renamed from use_libei; auto | libei | portal | wlr
keyboard_layout = "auto"
mouse_acceleration = false
enable_touch = false # MS-RDPEI multitouch on the libei path
[cursor]
Server-sent cursor shapes and positions, so the client draws the pointer itself. The section previously reached nothing outside the GUI editor; from 1.4.5 it builds a live cursor strategy.
| Key | Type | Default | Description |
|---|---|---|---|
| mode | string | "metadata" | metadata or painted. painted is accepted but behaves like metadata until server-side compositing exists |
| auto_mode | boolean | true | Switch to predictive movement once measured round-trip time exceeds the threshold |
| predictive_latency_threshold_ms | integer | 100 | Round-trip time above which predictive movement engages |
| cursor_update_fps | integer | 60 | Rate at which the cursor strategy ticks |
mode = "metadata"
auto_mode = true
predictive_latency_threshold_ms = 100
cursor_update_fps = 60
[clipboard]
Clipboard sharing settings between RDP client and server.
| Key | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | true | Enable clipboard sharing |
| protocol | string | "auto" | auto, ext (ext-data-control-v1), or wlr (wlr-data-control-v1) |
| file_transfer_mode | string | "auto" | auto, fuse, staging, or portal |
| allowed_types | array | ["text", "html", "image", "files"] | From 1.4.5 the html and image entries gate the eager fetch on the data-control path, so HTML and image pastes from Windows work there |
| max_size | integer | 10485760 | Bytes. A decoded image larger than this is not published rather than served partially |
enabled = true
protocol = "auto" # auto | ext (ext-data-control-v1) | wlr (wlr-data-control-v1)
allowed_types = ["text", "html", "image", "files"]
max_size = 10485760 # bytes
# file_transfer_mode = "auto" # auto | fuse | staging | portal
[advanced]
Advanced settings for PipeWire integration and GPU buffer management. Change only if you understand the implications.
| Key | Type | Default | Description |
|---|---|---|---|
| pipewire_buffer_count | integer | 4 | Number of PipeWire buffers for frame capture |
| dmabuf_enabled | boolean | true | Enable DMA-BUF for zero-copy GPU buffer sharing |
| explicit_sync | boolean | true | Enable explicit GPU synchronization (requires kernel 6.8+) |
pipewire_buffer_count = 4
dmabuf_enabled = true
explicit_sync = true
[logging]
Log output settings for diagnostics and debugging.
| Key | Type | Default | Description |
|---|---|---|---|
| level | string | "info" | Log level (error, warn, info, debug, trace) |
| file | string | "" | Log file path (empty = stderr only) |
level = "info"
file = "" # Log to stderr; set a path to also write to file
Renamed keys. input.use_libei is now input.input_protocol, and display.allow_rotation is now display.frame_transform. The old names are silently ignored, so correct any config that still carries them.
Editable in the GUI from 1.4.5. [capture] on the Video tab, [monitoring] under Performance, and [notifications] on the Status tab.
The shipped example-config.toml matches these defaults and is the reference for every key. A test enforces it.