Configuration
Polaris is designed to be configured from the web UI first. The config file is still useful when you want to script setup, review current values, or recover from a broken UI state.
| File | Default path | Purpose |
|---|---|---|
| Main config | ~/.config/polaris/polaris.conf |
Host settings |
| App library | ~/.config/polaris/apps.json |
Published apps and launch behavior |
| Runtime state | ~/.config/polaris/polaris_state.json |
Saved UI and session state |
If you change port in polaris.conf, the web UI moves to https://localhost:<port + 1>.
Add installed Steam titles from the web UI’s Applications library scan. The Nix modules do not
generate or replace apps.json; Polaris stores each imported title with its Steam app id and applies
the selected Linux stream runtime when that title launches. Steam Big Picture remains available as
the browse-first entry and does not require an app id.
Recommended first settings
Section titled “Recommended first settings”headless_mode = enabledlinux_use_cage_compositor = enabledlinux_prefer_gpu_native_capture = enabledtrusted_subnets = ["10.0.0.0/24"]encoder = nvencnvenc_split_encode_mode = disabledadaptive_bitrate_enabled = enabledmax_sessions = 2These are the settings behind the recommended Headless Stream mode on a Linux host. Use encoder = nvenc on NVIDIA, encoder = vaapi on AMD/Intel Mesa VAAPI hosts, and encoder = software only as a fallback or diagnostic path. Vulkan Video is experimental: an explicit choice supports DRM/KMS, wlroots, and Portal capture, while Auto promotes it only for a compatible AMD private-stream route that passes an exact live-frame safety probe.
The encoder key remains the host-wide default. Clients that advertise the typed session-encoder contract can ask Polaris to keep that default, use Auto, or require one backend for a single game. Polaris accepts only a backend compiled into the running build, validates it again against the live capture route at launch, and restores the host value at teardown. Auto is the only request allowed to fall back; a named backend fails closed instead of silently selecting another encoder.
Linux display modes
Section titled “Linux display modes”This is the config-file summary. For choosing a mode, what each one feels like in practice, and AMD/NVIDIA guidance, see Launch modes and capture paths.
| I want | Set linux_stream_mode to |
|---|---|
| My real desktop, at host resolution | desktop_display (Mirror Desktop) |
| An extra display, sized to the client | host_virtual_display |
| My live Hyprland desktop on a private client-sized output, with host displays dark | desktop_takeover |
| An isolated game-only session, desktop untouched | headless_stream / windowed_stream |
Two client-facing notes: Moonlight-protocol clients can request the mirror for a single launch with
mirrorDesktop=1 on /launch (no host reconfiguration), and headless_mode = enabled without
linux_use_cage_compositor derives host_virtual_display, not a headless session.
Common options
Section titled “Common options”| Key | Typical value | What it controls |
|---|---|---|
headless_mode |
enabled |
Request a stream-only session instead of the visible desktop |
headless_max_refresh_rate |
240 |
Refresh ceiling in Hz for launches where Polaris creates the display itself (Private Stream, Host Virtual Display, Desktop Takeover, Gamescope). Advertised to clients and enforced at launch from the same value; 0 uses the built-in default of 240 |
linux_use_cage_compositor |
enabled |
Enable Polaris’ private stream runtime |
linux_prefer_gpu_native_capture |
enabled |
Prefer DMA-BUF/GPU-resident capture on NVIDIA and AMD-capable stacks; Polaris reports SHM/system-memory fallback truthfully when the compositor or driver cannot provide it |
linux_stream_mode |
headless_stream (new installs) |
Stream path id for Linux sessions: headless_stream, windowed_stream, gamescope_stream, host_virtual_display, desktop_takeover, desktop_display, or headless_dongle. Empty derives the path from the legacy booleans above. See Launch modes and capture paths for choosing, stream paths for the contract |
linux_private_runtime |
labwc |
Private compositor used by paths that host the session themselves: labwc or gamescope. Ignored on host paths |
headless_swap_mode |
privacy |
Headless Dongle path only: privacy makes the dongle primary and blanks the panel after one-time portal approval is saved (the approval session keeps it on); off extends onto the dongle and leaves the panel primary |
linux_virtual_display_backend |
auto |
What creates the screen for Host Virtual Display: auto (a new KWin screen on KDE Plasma, then EVDI, then Hyprland, then kscreen-doctor borrowing linux_streaming_output), or evdi, kwin, wlr or kscreen to use only that one. A chosen backend that cannot run refuses the launch with its reason instead of falling back |
fallback_mode |
1920x1080x60 |
Display mode used when the client-requested mode is unsupported, as WxHxFPS. The web UI’s Display Planner presets write this same key, so Moonlight compatibility stays standard; Nova and per-game overrides can layer on top where client-settings support exists |
display_plan |
balanced |
Display Planner preset id persisted alongside fallback_mode: native, balanced, sharp, or performance. Empty means the mode was set manually; hand-editing fallback_mode clears the id so a stale pairing never lights up in the UI |
trusted_subnets |
CIDR list | Enable Trusted Pair on known local networks |
headless_gamepad_isolation |
enabled |
Hide host-connected gamepads from private headless streams; disable only when you intentionally want a wired host controller visible inside the stream |
client_gamepad_seat_isolation |
disabled |
Assign Polaris-created client gamepads to a dedicated Linux seat so other active-seat users do not receive automatic device ACLs |
client_keyboard_mouse_seat_isolation |
disabled |
Assign the virtual keyboard, mouse, touch and pen Polaris creates for clients to a dedicated Linux seat, so a client streaming a private session does not also type and click into the desktop session logged in at the machine |
mouse_cursor_visible |
enabled |
Composite a separately captured host cursor into the stream. Required for DRM/KMS; disable it if the client draws its own cursor and you see two pointers. Portal may embed its cursor independently |
back_button_timeout |
-1 |
Milliseconds, not seconds, that Back/Select must be held to emulate Home/Guide. -1 disables it. A small value such as 2 means two milliseconds, which turns nearly every Back/Select press into Home and makes the button look broken — use 2000 for two seconds |
capture |
empty (automatic) | Capture backend override; explicit Vulkan supports kms, wlr, and portal |
encoder |
nvenc / vaapi / vulkan / software |
Primary encoder backend; vulkan is experimental and strict when selected explicitly |
nvenc_split_encode_mode |
disabled |
Experimental Linux/FFmpeg NVENC split-frame encoding for HEVC/AV1 |
adaptive_bitrate_enabled |
enabled |
Allow mid-stream bitrate adjustment |
disconnect_resume_timeout_seconds |
300 |
Seconds to keep an app paused after client disconnect for resume |
max_sessions |
2 |
Number of simultaneous sessions or viewers |
enable_pairing |
enabled |
Accept new clients |
enable_discovery |
enabled |
Advertise Polaris over mDNS |
stream_audio |
enabled |
Capture and stream audio |
steamgriddb_api_key |
key | Cover art lookups for non-Steam apps |
beat_times_lookup |
enabled |
Ask How Long To Beat about titles missing from the local completion-estimate dataset; disable to keep the host from making those requests |
host_sleep_enabled |
disabled |
Let a paired client put this host to sleep |
Host sleep
Section titled “Host sleep”host_sleep_enabled lets a paired client suspend the host. It is off by default: a client putting
the host to sleep takes the machine away from everyone on it, and there is no undo from the couch.
With it on, POST /polaris/v1/host/sleep suspends the host. The request needs a paired client
certificate carrying launch permission, which every paired client has unless you made it watch
only, and Polaris refuses while a stream is running.
GET /polaris/v1/host/power reports what a client needs in order to decide whether to offer the
control at all:
- sleep_supported: logind answered that this host can suspend.
- sleep_enabled:
host_sleep_enabledis on. - sleep_permitted: the calling client may control this host rather than only watch it.
- sleep_blocked_reason:
polkit_denied,not_available,logind_unavailableorunsupported_platform. - sleep_blocked_message: the same thing in a sentence you can show someone.
The same block is served on /polaris/v1/capabilities, so a client that already polls capabilities
does not need a second request.
The General tab shows the same answer under the setting: whether this host can sleep, why not
when it cannot, with a link to the polkit rule below when polkit is the reason, and whether the last
sleep request was accepted but the host never went down. The console reads it from
GET /api/host/power, which carries the fields above except sleep_permitted, and
last_sleep_outcome (none, pending, suspended or failed) with last_sleep_message for the
last request.
Waking the host again is Wake-on-LAN, which Polaris does not do for you: enable it in the firmware and on the interface, and send the magic packet from the client.
Before you turn it on
Section titled “Before you turn it on”A host that sleeps and cannot wake is worse than one that never sleeps, and four separate things can cause that. Linux reports none of them as a failure, so check them rather than assuming. Every one of these was found on a working desktop that looked correctly configured.
1. The firmware wake setting, and a cold power-down after it. Enable the board’s wake option: on ASUS boards it is Advanced > APM Configuration > Power On By PCI-E/PCI, and other vendors call it Wake on LAN or Resume by PCI-E. If the board offers ErP Ready or EuP, it has to be disabled, because it cuts standby power to the slots.
Then shut the machine down fully, cut mains power for a few seconds, and start it again. Standby power to the PCIe slots is established at power-on, so a newly enabled wake setting does not take effect after an ordinary reboot. A host that was configured correctly and rebooted will still fail to wake.
2. The interface flag. ethtool <interface> | grep Wake-on should report g. Persist it with
nmcli connection modify <connection> 802-3-ethernet.wake-on-lan magic.
Do not read Wake-on: g as evidence that waking works. It says the driver armed the card. It says
nothing about whether the firmware will power that card while the host is asleep, and it reads
exactly the same on a host that cannot wake at all.
3. Devices that wake the host straight back. A trackball picks up desk vibration and a wireless receiver picks up stray RF, and either will wake the machine within seconds of every suspend, which looks like the host refusing to stay asleep. List what is armed:
for f in /sys/bus/usb/devices/*/power/wakeup; do [ "$(cat "$f" 2>/dev/null)" = enabled ] && echo "$f"doneTurn one off with echo disabled | sudo tee <that path>. That resets on every boot, so make it stick
with a udev rule matched on the device rather than on its port:
ACTION=="add|change", SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", ATTR{power/wakeup}="disabled"Leave at least one way to wake the host. Turning off every USB wake source on a machine whose Wake-on-LAN does not work leaves nothing but the power button.
4. Anything blocked on a network or FUSE mount. A single task that will not freeze aborts the whole suspend, and the host simply stays awake. The kernel blames the freezer rather than the process, so look for the name yourself:
journalctl -k | grep -A3 "refusing to freeze"A command walking a remote filesystem is the usual culprit, and an unscoped find / will find one.
Polaris reports this case: the suspend is accepted and then does not happen, and host_power carries
last_sleep_outcome of failed with a reason.
Testing it. Sleep the host once while you are standing next to it, and send a magic packet from another machine on the same network. If it comes back, the round trip works. If it does not, you are one power button press from trying again, which is the cheapest moment to find out.
When suspend works in a terminal but not from Polaris
Section titled “When suspend works in a terminal but not from Polaris”sleep_blocked_reason: polkit_denied means logind will only suspend after an interactive
authentication that a remote request cannot answer. That is the usual failure on a host where
systemctl suspend works fine in a terminal, because the terminal has an active local session and
Polaris may not. Allow the action for the user Polaris runs as:
polkit.addRule(function (action, subject) { if ((action.id == "org.freedesktop.login1.suspend" || action.id == "org.freedesktop.login1.suspend-multiple-sessions") && subject.user == "YOURUSER") { return polkit.Result.YES; }});Linux client-gamepad access boundary
Section titled “Linux client-gamepad access boundary”headless_gamepad_isolation controls the opposite direction: it hides controllers physically
connected to the host from a private stream. It does not make a client-created virtual controller
disappear from the host kernel.
When client_gamepad_seat_isolation is enabled, Polaris marks client gamepads for the bundled host
rules to assign them to the seat-polaris seat. This prevents logind from granting the active local
desktop user an automatic uaccess ACL. The device nodes remain root:input with mode 0660, so
the Polaris streaming user must belong to the input group. The option is disabled by default to
preserve existing virtual-controller identity and local access; enabling it gives isolated gamepads
Polaris-specific device names so current Inputtino uinput backends can enforce the udev policy.
Re-run sudo -H polaris --setup-host after upgrading so the installed udev rules understand the
dedicated device names and marker. Existing virtual controller nodes keep their previous access policy
until recreated; stop active streams and restart Polaris after host setup. AppImage users should
re-run the AppImage install action for the same reason.
The input group requirement is not optional and there is no fallback: the isolated nodes are
deliberately denied the logind ACL, so an account outside the group cannot open the devices Polaris
just created, and neither can the streamed game. Polaris logs an input_access: warning at startup
when seat isolation is enabled and the account it runs as is not a member, and --setup-host reports
the same thing. Add the account with sudo usermod -aG input <user> and log out and back in;
membership only applies to new sessions. On ostree hosts such as Bazzite the group is defined
in /usr/lib/group and usermod cannot see it, so use ujust add-user-to-input-group
instead. The startup warning prints whichever command applies.
client_keyboard_mouse_seat_isolation marks the virtual keyboard, mouse, touch and pen the same
way, but it has only the phys marker to work with: unlike gamepads, those devices keep their normal
names so a compositor configured to ignore them by name keeps working. The marker has to survive
into the kernel to matter, and a uinput backend that accepts the field without writing it leaves the
devices on seat0 while the setting still reads back as enabled. Polaris inspects the devices it
creates and logs a warning naming client_keyboard_mouse_seat_isolation when the marker never
arrived, so check for that line, or run the command below, before relying on the boundary.
Verifying that isolation is applied
Section titled “Verifying that isolation is applied”Check the device, not the seat list:
udevadm info -q property -n /dev/input/eventN | grep ID_SEATID_SEAT=seat-polaris means the rules applied and the device is isolated.
loginctl list-seats will keep showing only seat0, and that is expected. It is not a sign that
isolation failed. logind only materializes a seat that owns a device tagged master-of-seat, which
in practice means a graphics device. seat-polaris exists purely as a udev property that keeps
logind from handing the device to the seat0 session, so it never becomes a seat logind lists.
This is a Unix-user boundary, not a same-account process sandbox. Local users who are deliberately
members of input, and local applications running under the same Unix account as Polaris, can still
open the virtual controller. For concurrent gaming, run Polaris under a dedicated Unix account and
do not add local desktop users to input. Strong same-UID isolation requires a privileged broker,
container/security-domain boundary, or equivalent system-level policy; a per-session Web toggle
cannot provide it safely.
Linux host and private session input isolation
Section titled “Linux host and private session input isolation”Polaris can run a private stream session while somebody is using the desktop session logged in at the machine. Two separate leaks have to be closed for that to work, and they are closed by different mechanisms because the two directions are not symmetric.
Client input reaching the host desktop. The virtual keyboard and mouse Polaris creates are
kernel input devices, so a desktop session at seat0 receives them along with everything else on
that seat: the client types into the stream and into the desktop at the same time. Enabling
client_keyboard_mouse_seat_isolation marks those devices for the bundled udev rules to assign to
the seat-polaris seat, which stops logind from handing them to the seat0 session. Device names are
unchanged, so a host compositor already configured to ignore them by name keeps working. This is the
same Unix-user boundary described above: same-account processes and members of input are not
isolated.
Host compositors that do not go through logind can ignore the devices by name instead. In sway:
input 48879:57005:Polaris_Keyboard_passthrough events disabledinput 48879:57005:Polaris_Mouse_passthrough events disabledinput 48879:57005:Polaris_Mouse_passthrough_(absolute) events disabledHost devices reaching the private session. The reverse leak appears when the private compositor
opens the seat’s physical devices: the keyboard and mouse on the desk then drive the streamed
session. Polaris generates the private session’s ~/.config/labwc-polaris/rc.xml with a
<libinput> block that sets sendEventsMode to no for every physical device it finds, leaving its
own virtual devices enabled. This needs no configuration and applies to every private session.
Writing your own rc.xml there takes over completely: a file without Polaris’ generated marker
comment is never overwritten, and Polaris then stops managing input isolation for that session.
Steam Input and virtual controllers
Section titled “Steam Input and virtual controllers”Strict host-controller isolation exposes the Polaris virtual gamepad to the streamed app while hiding
physical controllers connected to the host. Local Steam Input settings can still claim that virtual
Xbox controller.
For Proton games, Steam then tries to hand the game a replacement controller through /dev/uinput,
but the strict sandbox deliberately does not expose that device or dynamically created input nodes.
The result is a controller that works in host-side event tests but is completely dead in the game.
When Doctor reports steam_input_conflict, open Steam Settings > Controller and disable Steam Input
for Xbox controllers. Also review the affected game’s Controller properties: use Default after
disabling the host-wide Xbox setting, or Disable Steam Input for that game. A per-game Force On
override still triggers the conflict.
This Doctor check is read-only. It reports only aggregate status and counts; it does not expose Steam account ids, installed app ids, profile filenames, or filesystem paths, and it does not edit Steam configuration. Close Steam before changing the setting through Steam or by another supported tool so the running client cannot overwrite the update.
Settings by tab
Section titled “Settings by tab”The web UI groups the keys above by area. Each Settings tab links to its section here.
General tab
Section titled “General tab”Host identity (the name clients see, the web console language, the log level), automation
(commands that run before an app launches and are undone after the session, commands that run
when the host changes session state, and server shortcuts a paired client may trigger), desktop
behaviour (pre-release update notifications, the tray icon, and whether the tray shows the force
stop, restart, and quit controls), and metadata integrations (a SteamGridDB key for artwork on
non-Steam entries, and whether Polaris may ask How Long To Beat for titles missing from its local
completion dataset). Keys: sunshine_name, notify_pre_releases, system_tray,
hide_tray_controls, steamgriddb_api_key, beat_times_lookup, host_sleep_enabled.
| Field | What it does |
|---|---|
Locale (locale) |
The locale used for Polaris’s user interface. |
Server Name (sunshine_name) |
The name displayed by Moonlight. If not specified, the PC’s hostname is used |
Log Level (min_log_level) |
The minimum log level printed to standard out |
Server Commands (server_cmd) |
Configure a list of commands to be executed when called from client during streaming. |
PreRelease Notifications (notify_pre_releases) |
Whether to be notified of new pre-release versions of Polaris |
Enable System Tray (system_tray) |
Whether to show Polaris icon in the system tray |
Hide tray control options (hide_tray_controls) |
Do not show “Force Stop”, “Restart” and “Quit” in tray menu. |
SteamGridDB API Key (steamgriddb_api_key) |
Optional API key used to fetch artwork metadata from SteamGridDB. The first-run wizard can check and save it. A saved key is used right away, by the cover search and by Nova, with no restart. |
Completion Estimate Lookups (beat_times_lookup) |
Allow Polaris to ask How Long To Beat about titles missing from its local completion-estimate dataset. Disabling it keeps the estimates already stored and stops the host making those requests on your behalf. |
Allow Clients To Sleep This Host (host_sleep_enabled) |
Let a paired client put this machine to sleep. Watch-only clients cannot. Polaris refuses while a stream is running. Waking it again is Wake-on-LAN. |
Input tab
Section titled “Input tab”Gamepads (whether clients may control the host with a controller, DualShock and DualSense
mapping, and the isolation switches that keep host-wired controllers out of private streams and
client pads off other Linux seats), keyboard (passthrough, repeat delay and rate, scancodes for
non-US layouts, right Alt as the Windows key), pointer and touch (mouse capture, the separately
composited host cursor that DRM/KMS capture needs, high-resolution scrolling, native pen and touch
events), and extras (an input-only app entry for TV workflows, rumble forwarding). Keys:
controller, headless_gamepad_isolation, client_gamepad_seat_isolation, keyboard,
key_repeat_delay, key_repeat_frequency, always_send_scancodes, mouse,
mouse_cursor_visible, high_resolution_scrolling, native_pen_touch,
client_keyboard_mouse_seat_isolation, enable_input_only_mode, forward_rumble,
back_button_timeout, multiseat_moonlight_input.
| Field | What it does |
|---|---|
Enable Gamepad Input (controller) |
Allows guests to control the host system with a gamepad / controller |
Emulated Gamepad Type (gamepad) |
Choose which type of gamepad to emulate on the host. Automatic follows the client’s controller: an Xbox, PlayStation or Nintendo pad gets its own kind, a Steam Controller gets a DualSense (its gyro and both touchpads fit there), and anything else gets a DualSense when it reports motion or a touchpad and an Xbox One pad otherwise. |
Emulate a DS4 gamepad if the client gamepad reports motion sensors are present (motion_as_ds4) |
If disabled, motion sensors will not be taken into account during gamepad type selection. |
Emulate a DS4 gamepad if the client gamepad reports a touchpad is present (touchpad_as_ds4) |
If disabled, touchpad presence will not be taken into account during gamepad type selection. |
Map Back/Select to Touchpad Click (ds4_back_as_touchpad_click) |
When forcing DS4 emulation, map Back/Select to Touchpad Click |
Randomize DualSense MAC Address (ds5_inputtino_randomize_mac) |
Randomize the emulated DualSense MAC address to improve compatibility with some games and clients. |
Isolate Host Gamepads in Private Stream (headless_gamepad_isolation) |
Prevents controllers physically connected to the host from appearing inside Private Stream sessions. Disable only when you intentionally use a host-wired controller, such as a DualSense for haptics or adaptive triggers. |
Isolate Client Gamepads from Other Linux Users (client_gamepad_seat_isolation) |
Disabled by default to preserve controller identity and local access. When enabled, uses Polaris-specific device names and a dedicated Linux seat so logind does not grant the active local desktop user automatic access. This does not isolate applications running under the same Unix account or users in the input group. Re-run Polaris host setup after upgrading, then stop active streams and restart Polaris so existing controller nodes are recreated. |
Experimental Moonlight Multiseat Input (multiseat_moonlight_input) |
Linux-only, file-configured preview gate for the host side of multiseat input. It is disabled by default and does not select a seat by itself; only a separately authenticated, already-admitted seat selection can use it. Leave disabled outside bounded development testing. |
Home/Guide Button Emulation Timeout (back_button_timeout) |
If the Back/Select button is held down for the specified number of milliseconds, a Home/Guide button press is emulated. If set to a value < 0 (default), holding the Back/Select button will not emulate the Home/Guide button. |
Enable Keyboard Input (keyboard) |
Allows guests to control the host system with the keyboard |
Key Repeat Delay (key_repeat_delay) |
Control how fast keys will repeat themselves. The initial delay in milliseconds before repeating keys. |
Key Repeat Frequency (key_repeat_frequency) |
How often keys repeat every second. This configurable option supports decimals. |
Always Send Scancodes (always_send_scancodes) |
Sending scancodes enhances compatibility with games and apps but may result in incorrect keyboard input from certain clients that aren’t using a US English keyboard layout. Enable if keyboard input is not working at all in certain applications. Disable if keys on the client are generating the wrong input on the host. |
Map Right Alt key to Windows key (key_rightalt_to_key_win) |
It may be possible that you cannot send the Windows Key from Moonlight directly. In those cases it may be useful to make Polaris think the Right Alt key is the Windows key |
Enable Mouse Input (mouse) |
Allows guests to control the host system with the mouse |
High Resolution Scrolling Support (high_resolution_scrolling) |
When enabled, Polaris will pass through high resolution scroll events from Moonlight clients. This can be useful to disable for older applications that scroll too fast with high resolution scroll events. |
Native Pen/Touch Support (native_pen_touch) |
When enabled, Polaris will pass through native pen/touch events from Moonlight clients. This can be useful to disable for older applications without native pen/touch support. |
Isolate Client Keyboard and Mouse from the Host Session (client_keyboard_mouse_seat_isolation) |
Disabled by default. When enabled, Polaris assigns the virtual keyboard and mouse it creates for clients to a dedicated Linux seat, so logind does not hand them to the desktop session logged in at the machine. Use it when you stream a private session while someone is working at the host: without it, a client’s typing and pointer movement also drive the host desktop. Applications running under the same Unix account and members of the input group are not isolated. Stop active streams and restart Polaris so the devices are recreated. |
Enable Input Only Mode (enable_input_only_mode) |
Add an Input Only app entry. When enabled, the app list will only show the current running app and the Input Only entry when streaming. The Input Only entry will not receive any image or audio. Useful for operating the desktop on TV or connecting peripherals which the TV doesn’t support with a phone. |
Forward Rumble Messages (forward_rumble) |
Forward Rumble Messages to clients |
Network tab
Section titled “Network tab”Exposure (whether the host announces itself for discovery, whether pairing is enabled, UPnP port
forwarding, the address family, the base port and the port map derived from it, which origins may
reach the web UI, and an external IP for clients outside the network) and transport security (the
encryption mode for LAN and WAN sessions, Trusted Subnet Auto-Pairing, and the trusted subnet
list in CIDR form). Keys: enable_discovery, enable_pairing, upnp, address_family, port,
origin_web_ui_allowed, external_ip, lan_encryption_mode, wan_encryption_mode,
trusted_subnets.
The first-run wizard’s Network step can trust a detected home network with one click, or a
network typed in CIDR form. It adds the network to trusted_subnets, keeping the entries already
there, and turns on Trusted Subnet Auto-Pairing (trusted_subnet_auto_pairing). A saved trusted
network applies to the next pairing request, with no restart. Any device on a trusted network can
pair without a PIN, so only trust networks you control.
| Field | What it does |
|---|---|
Enable Auto Discovery (enable_discovery) |
When disabled, you’ll need to manually enter host IP on the client to pair. |
Enable Pairing (enable_pairing) |
Enable pairing for the Moonlight client. This allows the client to authenticate with the host and establish a secure connection. |
UPnP (upnp) |
Automatically configure port forwarding for streaming over the Internet |
Address Family (address_family) |
Set the address family used by Polaris |
Port (port) |
Set the family of ports used by Polaris |
Origin Web UI Allowed (origin_web_ui_allowed) |
The origin of the remote endpoint address that is not denied access to Web UI |
External IP (external_ip) |
If no external IP address is given, Polaris will automatically detect external IP |
LAN Encryption Mode (lan_encryption_mode) |
This determines when encryption will be used when streaming over your local network. Encryption can reduce streaming performance, particularly on less powerful hosts and clients. |
WAN Encryption Mode (wan_encryption_mode) |
This determines when encryption will be used when streaming over the Internet. Encryption can reduce streaming performance, particularly on less powerful hosts and clients. |
Ping Timeout (ping_timeout) |
How long to wait in milliseconds for data from moonlight before shutting down the stream |
Audio and video tab
Section titled “Audio and video tab”Everything the stream is made of: the launch mode and its runtime, host audio capture and the
sink Polaris captures, the display outputs and fallback mode the Display Planner writes, Auto
Quality, advanced tuning such as the maximum bitrate and the adaptive range, and how long a paused
session waits for a client to resume. The mode cards, the live Auto Quality strip, and the planner
are explained in Launch modes and capture paths. Keys: linux_stream_mode,
headless_mode, linux_use_cage_compositor, linux_prefer_gpu_native_capture, fallback_mode,
display_plan, adaptive_bitrate_enabled, disconnect_resume_timeout_seconds.
Advanced tab
Section titled “Advanced tab”Load handling (limit the capture frame rate to what the client asked for), compatibility switches
for older tools and clients (environment variable compatibility, legacy app ordering, streaming on
even when encoder probing fails, the experimental browser stream), and the capture and encoder
overrides that force a specific path when automatic selection is wrong. Keys: limit_framerate,
envvar_compatibility_mode, legacy_ordering, ignore_encoder_probe_failure,
browser_streaming, encoder.
| Field | What it does |
|---|---|
FEC Percentage (fec_percentage) |
Percentage of error correcting packets per data packet in each video frame. Higher values can correct for more network packet loss, but at the cost of increasing bandwidth usage. |
Quantization Parameter (qp) |
Some devices may not support Constant Bit Rate. For those devices, QP is used instead. Higher value means more compression, but less quality. |
Minimum CPU Thread Count (min_threads) |
Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest value that can reliably encode at your desired streaming settings on your hardware. |
Limit capture framerate (limit_framerate) |
Limit the framerate being captured to client requested framerate. May not run at full framerate if vsync is enabled and display refreshrate does not match requested framerate. Could cause lag on some clients if disabled. |
ENVVAR compatibility mode (envvar_compatibility_mode) |
Enable compatibility mode for environment variables. This will modify the behavior of certain environment variables to be more compatible with older tools. |
App ordering for legacy clients (legacy_ordering) |
Enable ordering support workaround for legacy clients. Can cause issues with clients or scripts that can’t handle UTF8 correctly. Nova clients support this by default. |
Ignore Encoder Probe Failure (ignore_encoder_probe_failure) |
Allow streaming to continue even if probing for encoders fails. This may result in streaming failure if no encoder is available. |
Browser Stream (browser_streaming) |
Experimental zero-install LAN browser access over WebTransport and WebCodecs. Best for touch, mouse, keyboard, launchers, desktop, and slower games; use Nova or Moonlight for controller-first play. |
HEVC Support (hevc_mode) |
Allows the client to request HEVC Main or HEVC Main10 video streams. HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding. |
AV1 Support (av1_mode) |
Allows the client to request AV1 Main 8-bit or 10-bit video streams. AV1 is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding. |
Force a Specific Capture Method (capture) |
On automatic mode Polaris will use the first one that works. NvFBC requires patched nvidia drivers. |
Force a Specific Encoder (encoder) |
Force a specific encoder, otherwise Polaris will select the best available option. Note: If you specify a hardware encoder on Windows, it must match the GPU where the display is connected. The first-run wizard’s GPU and Encoder step shows what Automatic picks on this host and what hardware encoding still needs, and saves this key when you choose an encoder there. A changed encoder takes effect after a restart. |
Files tab
Section titled “Files tab”Where Polaris keeps its app library, its log, its runtime state, and, optionally, the web
credentials separately from the state file. The defaults are listed under Files. Keys:
file_apps, log_path, file_state, credentials_file.
| Field | What it does |
|---|---|
Apps File (file_apps) |
The file where current apps of Polaris are stored. |
Logfile Path (log_path) |
The file where the current logs of Polaris are stored. |
State File (file_state) |
The file where current state of Polaris is stored |
Credentials File (credentials_file) |
Store Username/Password separately from Polaris’s state file. |
Private Key (pkey) |
The private key used for the web UI and Moonlight client pairing. For best compatibility, this should be an RSA-2048 private key. |
Certificate (cert) |
The certificate used for the web UI and Moonlight client pairing. For best compatibility, this should have an RSA-2048 public key. |
AI and Encoder Profiles tabs
Section titled “AI and Encoder Profiles tabs”The AI tab is covered under AI provider settings; the encoder tabs under
NVIDIA NVENC Encoder and Linux HDR and Main10,
with the codec switches (hevc_mode, av1_mode), the quantisation fallback (qp), and the
software encoder thread floor (min_threads) on the encoder pages themselves.
Linux HDR and Main10
Section titled “Linux HDR and Main10”On Linux, treat sessions that log stream_hdr_enabled=false as SDR even if the client requests HDR.
Forcing hdr_mode = 2 can still select a 10-bit HEVC/Main10 or P010 encode path, but that does not
create a true HDR source when the captured display path is SDR and may produce incorrect colors on
some VAAPI stacks.
True Linux HDR requires the active capture path to expose HDR display metadata. Today that means a
KMS/DRM display path with an HDR-capable output reporting HDR_OUTPUT_METADATA, plus a client HDR
request and a 10-bit-capable encoder. A valid true HDR session logs:
HDR metadata: available=true usable=trueColor coding: HDR (Rec. 2020 + SMPTE 2084 PQ)HDR decision: ... display_hdr=true hdr_metadata_available=true stream_hdr_enabled=trueIf the log says HDR metadata: available=true usable=false, Polaris found an HDR
metadata blob but the static metadata is incomplete, such as a custom EDID with a
zero max luminance value. Polaris treats that stream as SDR instead of tagging it
as HDR with unusable metadata.
Headless labwc/wlroots sessions are intentionally treated as SDR until the headless display path can
truthfully provide HDR metadata. In that mode, hdr_mode = 2 can still be useful to test Main10/P010
encode support, but Polaris will not advertise true HDR to the client without metadata.
The configuration that carries true HDR today, verified end to end:
capture = kmslinux_stream_mode = desktop_displayhost_virtual_display, desktop_takeover and gamescope_stream also show the real output;
headless_stream and windowed_stream do not. KMS capture needs CAP_SYS_ADMIN on the binary,
granted once with sudo -H polaris --setup-host --enable-kms. The paired client must not have HDR
forced off in client_profiles.json (hdr) or device_db.json (hdr_capable), and the client has
to request HDR itself. The full checklist with the log line for each step is in
runtime.md.
For AMD VAAPI hosts, validate SDR first:
encoder = vaapihdr_mode = 0color_range = 1Then test HEVC Main 8-bit before enabling Main10 or client HDR requests.
NVIDIA NVENC Encoder
Section titled “NVIDIA NVENC Encoder”nvenc_split_encode_mode
Section titled “nvenc_split_encode_mode”Controls FFmpeg’s split_encode_mode private option for Linux NVENC HEVC and AV1 encoders. Polaris only
passes this option when the selected FFmpeg encoder exposes it; H.264 and native Windows NVENC ignore it.
Recommended values:
| Value | FFmpeg value | Recommendation |
|---|---|---|
disabled |
15 |
Default. Preserves legacy behavior after FFmpeg updates. |
auto |
0 |
Let NVIDIA’s driver and FFmpeg decide after validating your GPU/driver stack. |
2 |
2 |
Useful first manual test on multi-NVENC GPUs, especially for 4K120 HEVC/AV1. |
forced |
1 |
Experimental. Use only when comparing against auto and explicit engine counts. |
3 |
3 |
Experimental. Use only on GPUs known to expose three usable NVENC engines. |
Vulkan Encoder
Section titled “Vulkan Encoder”Vulkan Video is an experimental Linux hardware-encode path for drivers that expose the Vulkan Video encode extensions. Explicit selection supports direct DRM/KMS, wlroots, and Portal capture. GPU-native DRM/KMS and wlroots frames remain matched to the encoder’s render node; Portal and any safely retired wlroots DMA-BUF route use the Vulkan RAM uploader rather than pretending a CPU copy is zero-copy.
With encoder left on Auto, Polaris promotes Vulkan only for a compatible AMD private-compositor
route that can validate the exact first live GPU-native frame and retire a failed route to VA-API.
NVIDIA stays on NVENC, Intel stays on VA-API, and AMD desktop capture stays on VA-API by default.
Before selecting it, enable KMS host access once, restart Polaris, then set both overrides:
sudo -H polaris --setup-host --enable-kmscapture = kmsencoder = vulkanIf the selected GPU lacks the requested H.264 or HEVC Vulkan Video encoder, or an explicitly selected
capture path cannot initialize, session startup fails with an explicit diagnostic. AV1 stays disabled
on this experimental path until the bundled FFmpeg implementation passes Vulkan validation. Switch
the encoder back to vaapi or nvenc; Polaris does not silently replace an explicit Vulkan choice.
vk_tune
Section titled “vk_tune”Selects FFmpeg’s Vulkan Video latency/quality target. 2 (low latency) is the streaming default;
0 lets FFmpeg decide, 1 favors quality, and 3 requests ultra-low latency.
vk_rc_mode
Section titled “vk_rc_mode”Selects Vulkan Video rate control. 2 (constant bitrate) is the streaming default. 0 lets FFmpeg
and the driver decide, 1 selects constant-QP mode, and 4 selects variable bitrate.
AI provider settings
Section titled “AI provider settings”The AI optimizer is optional. Configure it in the web UI if you want connection testing before
saving, or set it directly in polaris.conf. The first-run wizard offers the same provider
choice, sign-in, model list, test and enable step; timeouts, the explanation cache and history
stay in the AI tab. Saved provider settings take effect right away; a request that is already running
finishes with the settings it started with.
Anthropic
Section titled “Anthropic”ai_enabled = enabledai_provider = anthropicai_model = claude-haiku-4-5-20251001ai_auth_mode = subscriptionai_timeout_ms = 30000For Claude subscription explanations, install a current Claude Code CLI
with --safe-mode support on the Polaris host. Sign in as the same OS user running Polaris:
claude auth loginclaude auth statusPolaris reuses that CLI subscription login. There is no separate Claude login in the web UI,
and a login on another computer or under root does not authorize the Polaris service account.
Choose Claude → Claude CLI, run Test provider, then save and enable explanations.
The saved-runtime status distinguishes CLI discovery from verified subscription authentication.
Doctor sends redacted evidence through a private, temporary request directory, with CLI tools, MCP servers, ordinary discovered customizations, and session persistence disabled. The installed, unmodified Claude Code and its administrator configuration are trusted host software: managed hooks and policy can still run with the service user’s permissions. This is not an OS sandbox. Polaris does not collect, copy, or intermediate subscription tokens; sign-in stays with Claude Code. Polaris validates the returned explanation and does not apply settings or run recovery actions from AI responses. This response boundary also applies to the other Doctor providers. Requests use the configured timeout, bounded to 1–120 seconds, and a 64 KiB output limit. Older CLIs that do not support the required restriction flags fail rather than retrying without them. This Doctor transport uses Claude subscriptions; Anthropic API-key Doctor explanations remain unsupported.
Subscription availability is provider-specific. OpenAI uses the signed-in Codex CLI below. DeepSeek’s documented integration uses an API key; signing into its chat website does not establish a supported Polaris subscription transport. Local endpoints can run without a provider account. See Claude Code authentication and credential use and DeepSeek’s API setup.
OpenAI
Section titled “OpenAI”With an API key, the hosted model names apply:
ai_enabled = enabledai_provider = openaiai_model = gpt-5.4-miniai_auth_mode = api_keyai_api_key = sk-proj-...With the signed-in Codex CLI, the models are the ones Codex offers the signed-in account, which
is a different set from the hosted API (a ChatGPT account cannot use gpt-5.4-mini through
Codex, for example). The console’s model list reads the catalog the CLI caches on this host, and
its default is the model the CLI is configured with. Leave ai_model empty to follow that
default, or pick one of the listed models:
ai_enabled = enabledai_provider = openaiai_auth_mode = subscriptionai_model =If the list is empty, run codex once in a terminal as the user Polaris runs as, then refresh.
When Codex refuses a request, the test result shows its reason.
Gemini
Section titled “Gemini”ai_enabled = enabledai_provider = geminiai_model = gemini-2.5-flashai_auth_mode = api_keyai_api_key = YOUR_GEMINI_KEYDeepSeek
Section titled “DeepSeek”Select the DeepSeek provider and DeepSeek API profile in the web UI, enter a DeepSeek API key, refresh the model list, and run Test Explanation before saving. To configure it directly:
ai_enabled = enabledai_provider = deepseekai_model = deepseek-v4-flashai_auth_mode = api_keyai_api_key = YOUR_DEEPSEEK_KEYai_base_url = https://api.deepseek.comai_timeout_ms = 30000The profile uses DeepSeek’s OpenAI-compatible endpoint for model discovery and chat completions, with JSON-object output and thinking disabled for bounded explanations. Polaris validates the returned explanation against the same six-field contract and keeps deterministic Doctor results as the source of truth.
If you previously entered a DeepSeek URL under OpenAI or Anthropic, select the DeepSeek API profile
and enter its key again. This sets the compatible output format and base URL; the Anthropic
/anthropic endpoint is not used by Polaris’s Doctor explanation path. Subscription login is not
supported for DeepSeek. Model discovery reflects the API’s current list; the configured default is
only a fallback when discovery is unavailable.
Local OpenAI-compatible server
Section titled “Local OpenAI-compatible server”ai_enabled = enabledai_provider = localai_model = gpt-ossai_auth_mode = noneai_base_url = http://127.0.0.1:11434/v1ai_timeout_ms = 60000Large local models can need substantially longer than cloud models for their first response while weights are loaded. The web UI’s local-provider profiles start with a bounded 60-second timeout and report inference timeout, connection, authentication, missing-model, and response-format failures separately. Lower the timeout after the model is warm if you prefer faster failure.
Rotate the web credentials
Section titled “Rotate the web credentials”Security in the web UI changes the sign-in for the web console only; paired clients are not affected. The page has two steps and one commit:
- Verify current access: type the current username and password. Polaris confirms the account before it changes anything.
- Choose new credentials: type the replacement password twice. Leave the username blank to keep the current one.
- Save and Reload: Polaris applies the new pair and reloads this browser session, so you sign in again immediately with what you just set.
The password fields have a Show control for checking what you typed. If you lose the credentials instead of rotating them, use the reset below.
Credential reset
Section titled “Credential reset”If you lose access to the web UI credentials:
polaris --creds new-username new-passwordRun the command as the same user account that runs Polaris, then restart Polaris before signing in with the new credentials:
systemctl --user restart polarisIf Polaris is running in the foreground, stop it and start it again instead.
Host setup helper
Section titled “Host setup helper”To re-run the host setup steps explicitly:
sudo -H polaris --setup-hostOptional DRM/KMS setup:
sudo -H polaris --setup-host --enable-kmsVA-API session controls
Section titled “VA-API session controls”These settings apply to VA-API on Linux. A successful web configuration save publishes the settings for the next encoder session; an active codec keeps its current settings. Hand-edited configuration is loaded when Polaris starts.
| Setting | Default | Choices |
|---|---|---|
vaapi_quality |
auto |
auto, speed, balanced, quality |
vaapi_rc |
auto |
auto, cbr, vbr, avbr, cqp, icq, qvbr |
vaapi_blbrc |
auto |
auto, enabled, disabled |
vaapi_strict_rc_buffer |
disabled |
enabled, disabled |
Automatic quality and block bitrate control leave the codec’s existing defaults untouched. Automatic rate control preserves Polaris’s current policy: Intel, AV1, and explicit strict-buffer requests prefer VBR with a single-frame buffer, then CBR, then CQP. Other paths prefer CBR, then VBR, then CQP with the existing buffer size. No new AMD or Intel default is promoted by these controls.
Quality presets use the selected driver profile and encoding entrypoint’s
reported range: quality selects level 1, speed selects the highest level, and
balanced selects half the range, rounded down with a minimum of 1. Unknown or
unsupported ranges retain the driver default. This adjusts encoding effort;
it is separate from the qp setting.
A manual rate-control choice must be supported by both the driver and codec.
Unsupported choices use automatic policy and produce a warning. A supported
manual mode overrides the Intel/AV1 buffer preference; vaapi_strict_rc_buffer
still requests a single-frame buffer. CQP, ICQ, and QVBR use qp for their
quality value. CQP and ICQ do not enforce a bitrate target; FFmpeg may ignore
buffer settings in modes that do not use a hypothetical reference decoder.
Block bitrate control requires the driver’s VA_RC_MB capability and a mode
other than CQP. An unsupported enable request is reported and disabled when
the codec exposes the option. Startup logs report the selected rate control,
selection policy, buffer choice, compression level, quality range, and block
bitrate control. Controls are queried for every encoder initialization, using
the selected profile and entrypoint. Existing DMA-BUF containment is preserved.
Example of an opt-in configuration:
vaapi_quality = balancedvaapi_rc = vbrvaapi_blbrc = enabledvaapi_strict_rc_buffer = enabledThese settings follow the libva quality and rate-control API and FFmpeg VA-API mode handling. Hardware acceptance on AMD and Intel is required before changing automatic encoder defaults.
Stable KMS connector selection
Section titled “Stable KMS connector selection”With KMS capture, output_name accepts the connector’s kernel name (for example
DP-1 or HDMI-A-1) when that name identifies one available output. The log
also lists a qualified form such as kms:pci-0000:01:00.0/DP-1. Qualification
uses the GPU’s PCI address, not its changing /dev/dri/cardN number. The
pci-0000:01:00.0/DP-1 shorthand is accepted too.
Use the qualified form when different GPUs expose the same connector name. An ambiguous or missing request fails; Polaris does not select another output. Named capture rechecks the opened GPU, connector, and CRTC during initialization and rechecks the connector binding while capturing. A disconnect or reassignment requires capture reinitialization. Replugging the same physical port can resolve its qualified name even when card or display enumeration order changes.
Existing numeric configurations retain the original enumeration positions. The list is not sorted or renumbered by the new names. A GPU without a reliable PCI identity, or a connector with multiple active capture planes, retains numeric selection. When unnamed entries remain, use qualified names for named requests; a plain connector alias cannot rule out ambiguity with those entries.
These identifiers select KMS capture outputs. They do not change the X11, Wayland private-runtime, Windows, or macOS display-selection contracts.