Bazzite Install Guide
Bazzite is Fedora-based, but it is an immutable rpm-ostree system rather than a
normal DNF-managed Fedora install. The clean Polaris path for everyday Bazzite
users is to layer the matching Fedora RPM, reboot into the new deployment, run
the host setup once, then start Polaris from a writable /usr/local copy when
DRM/KMS capture is needed.
This is still a validation path until Bazzite Desktop Mode, Game Mode, NVIDIA, AMD, and common Moonlight client flows have more real-hardware coverage. The install should be simple, but keep the rollback notes handy.
[!IMPORTANT] Use a Polaris release that includes an RPM matching your Bazzite Fedora base. Bazzite 44 should use
Polaris-fedora44-x86_64.rpm. If the latest release does not include your Fedora version yet, wait for the next release or use a tester build intentionally.
Validation Status
Section titled “Validation Status”| Image | Session | Result |
|---|---|---|
bazzite-nvidia-open:stable 44.20260430 |
KDE Plasma Wayland Desktop Mode | Polaris service, ports, Headless Stream launch, client profile application, and host-input isolation validated |
bazzite-nvidia-open:stable 44.20260430 |
Steam/Game Mode | Pending on a Game Mode-capable image |
The tested bazzite-nvidia-open:stable host is a Desktop image based on
Kinoite. It exposes only /usr/share/wayland-sessions/plasma.desktop to the
display manager. The host has gamescope, gamescopectl, gamescopestream,
bazzite-steam, and Steam installed, but it does not include a
gamescope-session package or a selectable Steam/Game Mode session.
That means this validation currently covers Desktop Mode only. Do not treat this image as real Bazzite Game Mode coverage until Polaris is retested on an image that can enter a gamescope Steam session from the host UI.
Install
Section titled “Install”If you already enabled Sunshine on Bazzite, stop it first. Sunshine and Polaris both use the default GameStream ports, so only one host should be running.
systemctl --user disable --now homebrew.sunshine.service 2>/dev/null || truesystemctl --user disable --now app-dev.lizardbyte.app.Sunshine.service 2>/dev/null || trueInstall Polaris from the Fedora 44 release RPM:
rpm_name="Polaris-fedora44-x86_64.rpm"wget --output-document="./${rpm_name}" "https://github.com/papi-ux/polaris/releases/latest/download/${rpm_name}" &&sudo rpm-ostree install -r "./${rpm_name}"After the reboot:
sudo -H polaris --setup-hostsystemctl --user stop polaris 2>/dev/null || truesudo install -D -m 0755 "$(readlink -f "$(command -v polaris)")" /usr/local/bin/polaris-kmssudo setcap cap_sys_admin+ep /usr/local/bin/polaris-kmsgetcap /usr/local/bin/polaris-kmsprintf '[Service]\nExecStart=\nExecStart=/usr/local/bin/polaris-kms\n' \ | systemctl --user edit --stdin --drop-in=10-bazzite-kms.conf polarissystemctl --user daemon-reloadsystemctl --user enable --now polarisOpen https://127.0.0.1:47990/#/welcome, create the web UI account, and pair
Moonlight, Nova, or another GameStream-compatible client. After credentials are
created, https://127.0.0.1:47990 opens the normal console.
This Bazzite-specific copy is intentional. Bazzite’s /usr deployment is backed
by composefs, so setcap can fail on the layered /usr/bin/polaris-* binary
even when run with sudo. /usr/local points into writable /var/usrlocal,
which can hold the capability-marked runtime copy used by the user service.
Re-run the /usr/local/bin/polaris-kms copy and setcap commands after each
Polaris package update so the service uses the newly installed binary.
If you want to test the EVDI virtual display path instead of the headless labwc path, pre-create one EVDI device before starting Polaris:
systemctl --user stop polarissudo modprobe -r evdisudo modprobe evdi initial_device_count=1cat /sys/devices/evdi/countls -l /dev/dri/card*systemctl --user start polarisThe expected result is cat /sys/devices/evdi/count returning 1 and an extra
/dev/dri/cardN whose driver is evdi. To make that survive reboots:
echo evdi | sudo tee /etc/modules-load.d/evdi.confecho 'options evdi initial_device_count=1' | sudo tee /etc/modprobe.d/evdi-polaris.confController and Input Group
Section titled “Controller and Input Group”Seat isolation (client_gamepad_seat_isolation, client_keyboard_mouse_seat_isolation)
needs the account Polaris runs as to be in the input group. Polaris warns at startup when
it is not.
sudo usermod -aG input $USER does not work on Bazzite. The input group is defined in
/usr/lib/group rather than /etc/group, so usermod cannot find a group to add anyone to.
Use the Universal Blue recipe, which copies the definition across first:
ujust add-user-to-input-groupThen sign out and back in — group membership only applies to new sessions.
id -nG | tr ' ' '\n' | grep -qx input && echo "in the input group" || echo "not in it"Thanks to @SVelothi for finding this.
Why rpm-ostree Layering
Section titled “Why rpm-ostree Layering”Polaris needs host-level integration: the binary, web assets, desktop metadata,
the user service, udev rules for virtual input, and compositor helpers such as
grim, labwc, wlr-randr, Xwayland, and xdpyinfo. On Bazzite, layering the RPM is
cleaner than running Polaris from a toolbox, distrobox, or unpacked archive
because the package manager can install those host dependencies into the booted
deployment.
The Polaris RPM declares the headless runtime dependencies, so the install
command should not need separate grim, labwc, or wlr-randr arguments.
Recommended Bazzite Optimization
Section titled “Recommended Bazzite Optimization”Start in Desktop Mode first. Game Mode and Deck-style gamescope sessions can hide display, portal, and environment details that are easier to debug from Desktop Mode.
Use Headless Stream for the first stream:
headless_mode = enabledlinux_use_cage_compositor = enabledlinux_prefer_gpu_native_capture = enabledThis is the recommended Bazzite Desktop Mode optimization for NVIDIA/NVENC and
AMD/Mesa VAAPI hosts. It creates an isolated headless labwc runtime for the
stream, routes launched apps and virtual input into that socket, and avoids
targeting the physical KDE desktop.
With linux_prefer_gpu_native_capture = enabled, logs may still report SHM/RAM
capture, CPU frame residency, or an extra CPU-side copy/conversion path when the
current compositor, driver, or encoder import path cannot stay GPU-native. Treat
those as performance notes, not startup failures, when the client receives a
stable stream from HEADLESS-1. If the setting prevents launch on a specific
AMD/NVIDIA stack, temporarily switch it to disabled and report the capture
decision JSON/logs.
Do not manually export WAYLAND_DISPLAY; Polaris starts labwc with its own
Wayland socket and routes launched apps into that socket. Do not add EVDI or
dummy-plug display routing for this validation path.
If you want to test a physical dummy plug instead, leave headless/labwc disabled and test it as a normal host display.
Desktop Mode Baseline
Section titled “Desktop Mode Baseline”On the tested NVIDIA Desktop image:
polaris.servicewas active under the user manager.- The service was enabled through
xdg-desktop-autostart.target. - A local drop-in launched
/usr/local/bin/polaris-kms. /usr/local/bin/polaris-kmshadcap_sys_admin=ep.- Polaris listened on
47984,47989,47990, and48010. - The active graphical session was KDE Plasma Wayland through
plasmalogin-autologin.
Baseline checks:
systemctl --user status polaris --no-pager -lsystemctl --user cat polarisgetcap /usr/local/bin/polaris-kmsgrep -E 'headless_mode|linux_use_cage_compositor|linux_prefer_gpu_native_capture' \ ~/.config/polaris/polaris.confloginctl list-sessionsloginctl show-session "$XDG_SESSION_ID" -p Type -p Desktop -p Class -p Statess -ltnup | grep -E '47984|47989|47990|48010'The Desktop Mode logs still reported the physical display:
Name: DP-3Found monitor: Samsung Electric Company Odyssey G95NCResolution: 7680x2160This is expected for the Desktop image before a client launches a headless labwc stream.
Game Mode Validation
Section titled “Game Mode Validation”Game Mode remains pending for bazzite-nvidia-open:stable Desktop images. A
valid Game Mode test host must expose a real Steam/Game Mode session, usually
through a gamescope session package and display-manager entry.
After entering Game Mode, verify Polaris before connecting a client:
systemctl --user is-active polarissystemctl --user status polaris --no-pager -lss -ltnup | grep -E '47984|47989|47990|48010' || truejournalctl --user -u polaris --since "5 minutes ago" --no-pagerThen connect with Nova at 1920x1080x60, followed by Moonlight or a Retroid
profile such as 1280x720x60. For each connection, collect:
journalctl --user -u polaris --since "3 minutes ago" --no-pager \ | grep -Ei "New streaming|stream_active|CLIENT|RTSP|session_event|labwc|HEADLESS|Steam|failed|Warning|Error"Success markers include:
Applying client profile for "<client name>"session_optimization: ... layers=client_profile+device_db+runtime_policylabwc: Starting in headless modelabwc: ReadySelected monitor [Headless output 1] for streamingWayland virtual input: routing supported devices to labwc socketEncoder cache saved: nvencNew streaming session startedsession_event: stream_activeCLIENT CONNECTEDSteam should report the client stream resolution, not the physical 7680x2160
DP-3 desktop.
There should not be a warning that virtual input is falling back to host uinput
during a healthy headless labwc stream. If that appears, stop testing and
report it as an input-isolation issue because host Plasma may receive remote
mouse or keyboard input.
If Polaris is inactive after entering Game Mode, treat it as a service or autostart packaging issue first:
systemctl --user restart polarissystemctl --user status polaris --no-pager -lIf Polaris is active but clients cannot discover or connect, verify listener ports and mDNS/Avahi from the Game Mode session before changing encoder code.
If clients connect but the stream is black, check whether logs mention
HEADLESS-1 or the physical display. DP-3 means app routing escaped the
headless labwc runtime. HEADLESS-1 means routing worked and capture or encoder
warnings should be inspected next.
Update
Section titled “Update”Layer the newer Fedora 44 RPM and reboot. rpm-ostree will stage the
newer local RPM over the existing layered Polaris package:
rpm_name="Polaris-fedora44-x86_64.rpm"wget --output-document="./${rpm_name}" "https://github.com/papi-ux/polaris/releases/latest/download/${rpm_name}" &&sudo rpm-ostree install -r "./${rpm_name}"Roll Back
Section titled “Roll Back”Bazzite keeps previous deployments. If the new deployment does not work, choose the previous deployment from the boot menu or run:
sudo rpm-ostree rollback -rUninstall
Section titled “Uninstall”Disable the user service before removing the layer:
systemctl --user disable --now polarissudo rpm-ostree uninstall -r polarisAfter rebooting, re-enable the Sunshine user service that matches the previous installation. The unit names are alternatives; run only the applicable command:
# Homebrew Sunshinesystemctl --user enable --now homebrew.sunshine.service
# Flatpak Sunshinesystemctl --user enable --now app-dev.lizardbyte.app.Sunshine.servicePolaris and Sunshine use the same default GameStream ports, so do not enable both hosts at the same time.
Known Bazzite Log Messages
Section titled “Known Bazzite Log Messages”labwc: No new Wayland socket appeared within 10s means the isolated labwc
runtime failed to start or exited before creating its Wayland socket. Confirm the
matching Fedora RPM was installed, rebooted into the new deployment, and retry
from Desktop Mode first.
Environment variable WAYLAND_DISPLAY has not been defined usually points to a
windowed Wayland runtime being launched without a parent Wayland session. In
private Headless Stream mode, Polaris can still start its own labwc socket for
the client; treat the message as a desktop-preview or portal-capture clue unless
the client stream itself fails to connect.
Couldn't scale frame ... src_fmt=bgr0 ... src_stride=0 means Polaris received a
CPU BGR0 frame without a valid row pitch. Use a release newer than v1.0.4, where
the headless CPU fallback path was fixed.
Failed to gain CAP_SYS_ADMIN with KMS probe could not access DRM framebuffer handles; continuing with non-KMS capture backends when available is only a
startup probe warning for portal/compositor users. Do not apply setcap for the
normal portal path.
KMS display capture requires CAP_SYS_ADMIN is actionable only when you
intentionally selected explicit KMS capture. On Bazzite, copy the current
packaged binary to /usr/local/bin/polaris-kms, apply setcap there, and make
sure the ~/.config/systemd/user/polaris.service.d/10-bazzite-kms.conf override
points ExecStart at that file.
Virtual display: failed to open EVDI device usually means the EVDI kernel
module is loaded without a pre-created DRM card. Load EVDI with
initial_device_count=1 and confirm that /sys/devices/evdi/count returns 1
before starting Polaris.
Virtual display: could not determine EVDI output name, using fallback [VIRTUAL-1] means Polaris could not map the opened EVDI card to its DRM
connector. On Bazzite with a pre-created device, the connector should look like
card1-DVI-I-1 under /sys/class/drm.
wlr: Using RAM capture path because this build does not include a GPU-native uploader for the selected encoder and capture will incur an extra CPU-side copy/conversion path are not startup failures. Confirm the stream is connected
by looking for session_event: stream_active, CLIENT CONNECTED, Selected monitor [Headless output 1], and Found H.264 encoder: h264_nvenc [nvenc].
For performance reports, though, treat capture_transport=shm frame_residency=cpu frame_format=bgra8, target_residency=cpu, or Build features: cuda=disabled with NVENC as important clues because they mean Polaris
is taking a CPU copy/upload path.
For NVIDIA true-headless performance testing, the fast path should report
Build features: cuda=enabled, capture_transport=dmabuf frame_residency=gpu,
and target_device=cuda target_residency=gpu. In the web UI or
/polaris/v1/session/status, capture.reason=headless_extcopy_dmabuf is the
desired true-headless marker; headless_shm_fallback means the stream can still
be healthy, but it is using the conservative CPU-side capture path.
display_preview: Failed to capture cage screenshot affects the web dashboard
preview path. It does not mean the Moonlight/Nova stream failed if the client is
already connected and receiving frames. Polaris rate-limits repeated preview
capture failures, but if the preview itself matters, include command -v grim
with the report.
If local Plasma receives remote mouse or keyboard input while using headless labwc, treat that as an input-isolation bug and include the validation details below.
Validation Checklist
Section titled “Validation Checklist”Please include these details when reporting Bazzite issues:
- Bazzite image name and version from
rpm-ostree status - Desktop Mode or Game Mode
- GPU model and driver stack
- Polaris RPM asset used, such as
Polaris-fedora44-x86_64.rpm - output of
command -v polaris grim labwc wlr-randr - the
Build features: cuda=...line - output of
getcap /usr/local/bin/polaris-kms - output of
systemctl --user cat polaris - whether
sudo -H polaris --setup-hostcompleted successfully - whether
systemctl --user status polarisis running - whether the web UI opens at
https://127.0.0.1:47990 - client used for pairing, such as Steam Deck Moonlight, Android Moonlight, or Nova
- active capture path shown in the Polaris dashboard
- requested client resolution, FPS, codec, and whether the web UI preview was open
- whether headless mode and virtual display behavior worked after a reboot