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
[server]
# 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
[security]
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
[audio]
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
[egfx]
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
[hardware_encoding]
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
[display]
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
[performance]
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
[damage_tracking]
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]
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
[cursor]
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
[clipboard]
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+)
[advanced]
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)
[logging]
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.