Vibepollo's Linux host is in beta. It ships as a native package for Arch Linux and CachyOS, and it is developed and tested on CachyOS with KDE Plasma 6 on Wayland. Other Arch-based distributions should work but get less testing. There is no AppImage, Flatpak, Debian, Fedora, or Docker build in this beta.
An explicit, experimental Hyprland physical-display subset is available through the manual native-package path. It requires UWSM and does not provide private outputs, HDR or Steam/Xwayland application launching.
| Requirement | Detail |
|---|---|
| Distribution | Arch Linux or CachyOS, x86_64. Vibepollo is tuned for CachyOS. |
| Desktop | KDE Plasma 6 on Wayland, started by SDDM or Plasma Login Manager. GNOME, other compositors, X11 sessions, and remote logins are not streamed. |
| Kernel | Linux 6.16 or newer, plus the headers for the kernel you boot (for example linux-cachyos-headers). The virtual-display driver is built with DKMS during installation. |
| GPU | A GPU with a hardware H.264 encoder. Testing has focused on NVIDIA and modern AMD GPUs; older cards may not work properly. NVIDIA uses NVENC through nvidia-utils; AMD needs libva-mesa-driver; Intel needs intel-media-driver. HEVC and AV1 are used when available. |
| Pre-login streaming | NVIDIA only. AMD and Intel machines stream after you log in. |
| Accounts | One interactive desktop account. Machines with several accounts choose the streaming owner once (see Choosing the streaming user). |
| Secure Boot | Supported. The package signs its kernel module; on kernels that enforce module signatures you approve a one-time MOK enrollment at the next reboot. Do not disable Secure Boot. |
Vibepollo runs as machine-wide system services, not as a per-user service. Do not run it with
systemctl --user, and do not add file capabilities to /usr/bin/vibepollo.
The installer checks the requirements above, installs the kernel headers for your running kernel, installs the package, opens the firewall if one is active, and tells you whether a reboot is needed.
curl -fsSLO https://raw.githubusercontent.com/Nonary/Vibepollo/master/scripts/linux_install.sh
less linux_install.sh # optional: read what it does
sudo bash linux_install.shThe script prefers the signed Nonary pacman repository. When the repository is not reachable it
downloads the newest release package from GitHub and installs it with pacman -U. Useful options:
| Option | Effect |
|---|---|
--version 2.0.0 |
Install that exact release. |
--stable |
Ignore pre-releases. |
--package ./vibepollo-*.pkg.tar.zst |
Install a package you already downloaded. |
--no-repo |
Skip the pacman repository and use GitHub releases. |
--yes |
Answer pacman prompts automatically. |
Re-running the script is safe. It only installs what is missing and repeats the checks.
The script does not run a full system upgrade. Local packages use pacman -U;
repository installs use existing metadata to install Vibepollo and its dependencies.
If the host repository has not been cached yet, the script uses a release package
instead of refreshing system databases automatically.
Maintain the rest of your Arch system separately. Matching headers for the
running kernel are required: installing headers for a newer kernel is not enough.
The script verifies the virtual-display module after package installation and
builds it if necessary; a missing module or build failure stops installation.
Unowned driver-source files left by earlier local updates inside a host-package-owned
/usr/src/vibeshine-drm-* directory are backed up under
/var/tmp/vibeshine-driver-backup.* before exact-path replacement. Backups remain
available if pacman fails. Other packages' files and symlinks are not overwritten.
Import and locally trust the Nonary repository key:
curl -fsSLo /tmp/nonary-vibepollo.gpg \
https://nonary.github.io/Vibepollo/arch/x86_64/nonary-vibepollo.gpg
curl -fsSLo /tmp/nonary-vibepollo-fingerprint.txt \
https://nonary.github.io/Vibepollo/arch/x86_64/nonary-vibepollo-fingerprint.txt
sudo pacman-key --add /tmp/nonary-vibepollo.gpg
sudo pacman-key --lsign-key "$(tr -d '[:space:]' </tmp/nonary-vibepollo-fingerprint.txt)"Add the repository and install:
sudo install -Dm644 /dev/stdin /etc/pacman.d/vibepollo.conf <<'EOF'
[vibepollo]
SigLevel = Required
Server = https://nonary.github.io/Vibepollo/arch/x86_64
EOF
grep -qxF 'Include = /etc/pacman.d/vibepollo.conf' /etc/pacman.conf || \
printf '\nInclude = /etc/pacman.d/vibepollo.conf\n' | sudo tee -a /etc/pacman.conf
sudo pacman -Syu vibepolloLater releases then arrive through the normal pacman -Syu.
Every release on the releases page carries a
vibepollo-<version>-1-x86_64.pkg.tar.zst asset. Install the kernel headers first so the driver
builds during the transaction, then install the package:
sudo pacman -S --needed "$(pacman -Qqo /usr/lib/modules/$(uname -r)/vmlinuz)-headers"
sudo pacman -Syu
sudo pacman -U ./vibepollo-*.pkg.tar.zstPackages installed this way are upgraded by downloading the next release and repeating the
pacman -U step, or by switching to the repository above.
The PKGBUILD in the repository is a template; a CMake configure step fills in the commit and
version before makepkg can use it. Install cuda first if you want NVENC (the build detects it):
git clone --branch 2.0.0 https://github.com/Nonary/Vibepollo.git
cd Vibepollo
cmake -S . -B build -DSUNSHINE_CONFIGURE_ONLY=ON -DSUNSHINE_CONFIGURE_PKGBUILD=ON
mkdir pkg && cp build/PKGBUILD build/vibepollo.install pkg/
cd pkg && makepkg -siThe build takes a while: it compiles the web interface with npm, runs the test suite, and needs
nodejs, npm, ninja, and gcc15 (the compiler CUDA supports). Pass --nocheck to
makepkg to skip the tests.
The package prints its remaining steps at the end of the pacman transaction. On a typical machine there are four.
-
Reboot after the first installation. The login screen and desktop must restart to load the new display and session integration, even if the driver is already loaded. On updates, reboot if asked. A kernel that still holds an older driver, or a pending Secure Boot key enrollment, needs one reboot. On the Secure Boot path the firmware shows the blue MOK Manager screen: choose Enroll MOK, continue, and enter the password you typed during installation.
-
Pair Moonlight. Open https://localhost:47990 on the machine, create the Web UI username and password, then enter the Moonlight PIN. The Web UI stays reachable at the login screen, so you can pair from another device before logging in. Log in to Plasma (Wayland) for desktop streaming.
-
Open the firewall if one is enabled. The package ships definitions for both common firewalls:
# firewalld sudo firewall-cmd --permanent --add-service=vibepollo && sudo firewall-cmd --reload # ufw sudo ufw allow Vibepollo
Vibepollo listens on TCP 47984, 47989, 47990, and 48010, and on UDP 47998 to 48000 and 48010. The UDP ports carry the video, audio, and control streams and are required even when the Web UI already works.
-
Log out and back in once. The package installs a PipeWire drop-in (
/usr/share/pipewire/pipewire.conf.d/50-vibepollo-audio.conf, 240 samples at 48 kHz) that the audio stream expects. A new login, orsystemctl --user restart pipewire, applies it.
Installation picks the single interactive desktop account automatically. If the machine has several accounts the package prints an ACTION REQUIRED line instead. Choose the owner once and enable the controller:
sudo vibepollo configure USER
sudo systemctl enable --now vibepollo-session-controller.serviceCustom application commands and per-app preparation commands require administrator approval.
After reviewing the commands saved in /var/lib/vibepollo/apps.json, approve that snapshot:
sudo vibepollo authorize-commandsRun this again after adding or changing commands. Saving an application in the Web UI does
not approve it. This command collects application launch, detached and preparation commands
from apps.json; it does not authorize global preparation commands from vibepollo.conf.
An unapproved command is rejected by the session broker and can exit with status 126.
Managed Steam and Lutris launches use fixed numeric-ID operations instead of approving
arbitrary commands. The exact reserved helper syntax, such as
/usr/libexec/vibeshine/vibepollo-session-exec lutris 16, selects that fixed operation even
when entered manually. It does not approve other command strings or shell arguments.
On a Linux machine-service host, run Lutris synchronization once after upgrading to refresh
older managed entries. Other custom launch and preparation commands still require approval.
sudo systemctl status vibepollo-session-controller.service vibepollo.service
sudo journalctl -u vibepollo-session-controller.service -u vibepollo.service -b
sudo vibepollo driver statusA healthy host logs Screencasting with KMS, Using event-driven KMS capture for vibeshine_drm CRTC, and at least Found H.264 encoder. HEVC and AV1 lines appear only when the GPU supports
them. Open ports alone do not prove streaming works; check for those lines.
The controller starts the host only while the configured user's Plasma Wayland session (or the
NVIDIA pre-login greeter) is active. Do not start vibepollo.service by hand. Restarting either
unit disconnects any client that is streaming.
| Symptom | What to do |
|---|---|
kernel headers ... are missing during install |
Run the printed sudo pacman -S --needed <kernel>-headers, then sudo vibepollo driver install. |
| Install says the running kernel still holds the old module | Reboot. modinfo -F version vibeshine_drm and /sys/module/vibeshine_drm/version must match. |
| MOK enrollment queued | Reboot and approve it in MOK Manager. mokutil --list-new shows what is pending. |
Controller logs unsupported-session |
You are not in a Plasma Wayland session started by SDDM or Plasma Login Manager. Pick "Plasma (Wayland)" at the login screen. |
| No HEVC or AV1 encoder found | Normal on older GPUs. H.264 is enough for the host to report ready. |
| No pre-login stream | Expected on AMD and Intel GPUs. Log in first. |
| Audio crackles or drifts | Log out and in again so the PipeWire drop-in applies. Keep Vibepollo's virtual sink; do not pin a static audio_sink. |
| Moonlight sees the host but the stream fails | Open the UDP ports (see above). |
could not start the Vibepollo session controller safely at install |
Check the reported cleanup proof, retained transaction record, and administrator masks, then retry the installer. Report the controller journal if it still fails. |
KMS is recommended for managed virtual screens for maximum compatibility and
Vibepollo's low-latency capture. Automatic capture selects KMS for these screens.
Explicit capture selections in /var/lib/vibepollo/vibepollo.conf are honored
for both physical and virtual screens. Other methods may not work properly with
virtual screens and do not use the low-latency KMS path. Portal capture can open
a consent dialog and block unattended startup.
Two things on the host machine will silently stop streaming:
- Automatic suspend. KDE's power settings suspend an idle machine even while Vibepollo is waiting for clients. On a streaming host, set "When inactive" to "Do nothing" under System Settings, Power Management, or at least disable it for the AC-powered profile.
- A second desktop session for the same user. A VNC or RDP server that starts its own Plasma
X11 session (
vncserver@:1.service,plasma-x11-session) imports that session's environment into your systemd user manager and stops the Wayland workspace target. The controller then reportswaiting for desktop session N to become usableand never starts the host. Stop and disable that service; Vibepollo's own pre-login stream is the supported remote path.
Settings live in /var/lib/vibepollo, including vibepollo.conf, credentials,
pairings, applications, and imported covers. Use the Web UI for routine changes.
Run vibepollo paths to list locations, vibepollo status for service status, or
sudo vibepollo logs for recent logs. vibepollo maintenance-help lists all
maintenance commands. Program files, administrator policy, and temporary session
data retain their separate ownership and lifetimes:
| Path | Purpose |
|---|---|
/usr/bin/vibepollo |
Public command-line client, unprivileged. |
/usr/libexec/vibeshine/ |
Privileged helpers (host, session broker, controller, driver installer). |
/etc/vibepollo/ |
Administrator policy, including the chosen desktop user in machine.conf. |
/var/lib/vibepollo/ |
Host state: vibepollo.conf, credentials, pairings, application list. |
/usr/src/vibeshine-drm-* |
DKMS source for the virtual-display driver. |
Repository installs upgrade with sudo pacman -Syu. Release-package installs upgrade with
sudo pacman -U on the next package. The upgrade hook stops the running host, so finish any stream
first. Upgrade hooks prepare the same profile for DEB, RPM, and Arch packages:
- The selected user's
~/.config/vibepollois imported once into/var/lib/vibepollo; the source copy is retained as a backup. - An existing marked machine profile takes precedence, so later upgrades never replace newer credentials, pairings, settings, or applications with the old copy.
- Old ownership and permissions are repaired before validation. Imported cover paths are normalized, and existing command approvals are preserved.
- Unsafe or failed imports stop setup instead of silently creating a fresh identity. A nonempty, unmarked destination is not overwritten.
For manual recovery after resolving a reported migration error, stop streaming and
stop the controller and host before running sudo vibepollo migrate, then enable
and start vibepollo-session-controller.service again. These service operations
end active streams. The old helper paths remain available for existing scripts.
If the hook says the kernel still holds the old driver, reboot before streaming again.
Package replacement deliberately disconnects streams. The host receives a graceful stop with a 60-second deadline. Replacement requires the old host's entire service cgroup to be empty, followed by broker/application cleanup and removal of the old session record.
The installer offers one force-stop retry after a recorded shutdown deadline when it has an interactive
terminal. --yes never accepts that choice automatically. For an unattended retry, use:
sudo bash linux_install.sh --package /path/to/fixed.pkg.tar.zst --force-stop --yesForce terminates the verified host service cgroup after the graceful deadline. Active streams
and applications end, and normal cleanup may be incomplete. Before the forced signal, the hook
saves a private snapshot of /etc/vibepollo and /var/lib/vibepollo, excluding changing logs,
under /var/lib/vibepollo-package-backups/forced-stop.*. The directory is root-only, and its
recovery.log records the old invocation, installed version, and force outcome. This snapshot
may have been taken while the old host was running; do not treat it as an atomic database backup.
Archive errors, including a missing tar, stop the forced operation.
Updated package hooks also accept VIBEPOLLO_PACKAGE_FORCE=1, for example
sudo env VIBEPOLLO_PACKAGE_FORCE=1 pacman -U /path/to/fixed.pkg.tar.zst.
That variable does not retrofit force support into an older installed hook; use the incoming
installer when upgrading a broken old package. Force never targets a process by name or an
unrelated session. A changed/uncertain service identity, an unkillable process, a populated
cgroup, or unresolved generation cleanup still blocks replacement. The failure report explains
the remaining proof and preserves the transaction record for a retry.
A host using the old executable outside the machine service blocks replacement even with force. The report identifies its PID and scope. An executable match does not establish authority over another user manager's restart policy, session cleanup, or profile backup; stop that exact host and fence its activation source through its owning manager before retrying. An inode absence scan alone cannot prevent an unfenced user service from restarting.
Cancellation waits for pacman and its hooks to finish before recovery. The client runs in a separate session so terminal interrupts cannot orphan a pretransaction hook. A requested cancellation may allow an in-progress package replacement to finish; the installer still returns the cancellation status.
Owned runtime masks and the upgrade fence are recorded under /run/vibepollo-package-transaction.
Failure/cancellation restores only barriers created by that transaction after verifying a safe
release. Existing administrator masks are preserved, so automatic startup may be deferred. An idle, empty host behind a verified administrator mask does not need a new shutdown fence; package replacement still requires the complete old-generation resource proof. Runtime and persistent masks stay in place, and configuration succeeds with automatic startup deferred. A masked host that is active, populated, has pending jobs, or cannot be inspected remains blocked with a diagnostic.
An unrecorded legacy fence is adopted only when the exact known fence and all four runtime masks match, with the old generation fully drained and no pending jobs. An active host, partial mask set, or different fence remains blocked even with force: ownership and the loaded shutdown contract are not established. Stop the identified old generation through its owning manager and verify its remaining jobs and resources before retrying; the installer preserves ambiguous barriers rather than removing them.
An interruption that leaves shutdown uncertain retains the record until a retry can finish; do not delete it or unmask every service indiscriminately.
The incoming installer extracts its shutdown guard from the selected package and overrides only
00-vibepollo-quiesce.hook in a private ALPM hook directory. It runs before package files change.
The installer queries pacman-conf HookDir and explicitly retains the effective default and configured hook directories alongside its private guard. Configured administrator overrides keep their normal precedence. An existing administrator hook with the private guard's exact filename causes a clear refusal before the transaction; it is neither overridden nor allowed to disable the required shutdown proof. A same-name administrator hook could disable the required shutdown proof, so that collision is reported before a transaction instead of overriding the administrator policy. Relative hook directories are refused because the installer and package client may have different working directories. If that read-only query is unavailable or fails, the installer refuses to start the transaction rather than omit administrator hooks.
The named upgrade-2.0-lifecycle-v1 compatibility step applies only to installed 2.0.0 builds
whose outgoing hook still has locale-sensitive metadata parsing or helper-text-first selection.
It selects an exact-name override of the known old ALPM hook and uses the incoming guard's full shutdown proof in the C locale. A regular incoming guard runs for other versions while their outgoing hooks retain their checks. It does
not edit installed hooks or suppress a failed proof. Fixed daily builds with the same version
are tested by their actual hook conditions. The step is repeatable and can be removed when
upgrades from those affected builds are no longer supported; future exceptions belong in
separate named, condition-scoped scripts under scripts/linux/migrations.
The separate upgrade-2.0.1-lifecycle-v1 step applies to 2.0.1 builds with the qualified helper-first selector and old fence without Restart=no. Both repository baselines are exactly tagged 2.0.1; the tests retain their original hooks, reproduce the direct fence-preparation mismatch, and verify the scoped override. Complete historical removal callbacks accept the proven incoming fence through their preserved-quiescence shortcut, keep the fence and masks intact, and reach driver cleanup. Epochs and package revisions are included in version matching. Unknown versions and already fixed hooks keep their outgoing checks.
The temporary upgrade-2.0.2-fuse-scan-v1 step supports the published
2.0.2daily.d20261017.2 Arch/CachyOS packages (package release 1). It matches
that daily, its published short commit suffix (with optional full .r suffix),
and the complete installed arch-package-hooks SHA-256 for
its qualified source commit. The new installer stages the corrected incoming
full guard under 00-vibepollo-quiesce.hook in its private HookDir, with
AbortOnFail retained. It never sources or edits the old installed library. The preflight rechecks
the version, complete fingerprint and file type under the package transaction
lock; a changed installed guard aborts before any new shutdown begins.
Edited, symlinked, already fixed, unknown-version, or different-revision hooks
are not given this override. Administrator same-name hook collisions still
abort before a transaction. Retire this step when upgrades from this daily
are no longer supported; qualify additional versions and artifacts separately.
Use a new installer containing this step with a fixed package containing both
the migration and corrected executable proof. The signed repository route and
--package FILE route both stage the guard before starting the package
transaction. For an already downloaded trusted fixed package, the route is:
sudo bash ./linux_install.sh --package ./vibepollo-FIXED_VERSION-1-x86_64.pkg.tar.zstAn installed failing AbortOnFail guard runs before an incoming package's
scriptlet can repair it. Direct pacman -U therefore cannot bootstrap this
fix while that installed guard still fails. Do not disable transaction hooks,
rename the installed library, or unmask uncertain generations to get past it.
The bootstrap must pass the corrected full process/session/cgroup proof before
replacement, and a failed proof still blocks file-security finalization.
Cleanup treats a nonempty reported cgroup as absent only after an errno-aware
lookup confirms it is missing. Permission and I/O errors remain blocked; a
real missing cgroup and a readable populated 0 group retain their behavior.
The same check covers the controller, machine-host helper and package hooks.
Controller cleanup also accepts a masked failed unit only when its MainPID is zero and its cgroup is empty or absent. Active, populated, malformed or uninspectable units remain blocked. A later cleanup failure now reports its actual step; older hooks could retain the label of a controller check that had already passed. The reported label alone does not establish which runtime resource prevented cleanup. These changes do not justify removing unknown fences or administrator masks.
The published 2.0.0 metadata failure compared translated stat %F text with regular file.
Locale-independent metadata checks are already present in the qualified primary source. The
new changes address idle helper selection, bounded force, transaction ownership and recovery.
Package ordering limits what an incoming package can repair:
| Path | Earliest applicable step |
|---|---|
| Arch/CachyOS installer | Private incoming PreTransaction guard, before outgoing hooks and package writes. |
| Future direct Arch transactions with the installed guard | Installed AbortOnFail hook loads the package function library before extraction. |
| Direct Arch upgrade/removal from a broken installed hook | Use the incoming installer preflight; incoming post-install code runs too late. |
| DEB | Old prerm upgrade runs before new preinst; dpkg can call new prerm failed-upgrade when the old script fails. Current removal-only prerm does nothing for those upgrade arguments. |
| RPM | New %pre runs before replacement; new %post precedes old %preun during upgrade. Old scriptlets can still affect the transaction. |
These boundaries follow the ALPM hook rules, Debian maintainer-script ordering, and RPM upgrade ordering. The shell entrypoint supports Arch/CachyOS. Its historical-package harness validates the incoming override and refusal paths; actual package-manager and GPU behavior requires host qualification. It does not claim to override arbitrary old DEB/RPM scripts or administrator hook overrides. The latest reporter did not establish their installed version, so the known 2.0.0 workaround cannot be assumed to match that report.
sudo pacman -R vibepolloFor a broken older pretransaction hook, supply a fixed incoming package as a source of the shutdown policy without installing it:
sudo bash linux_install.sh --remove --package /path/to/fixed.pkg.tar.zstAdd --force-stop only when you accept the termination consequences above. The removal path
does not run install checks, install headers, configure a package repository, or open the
firewall. Outgoing removal scriptlets still run; their behavior must be qualified with the
actual old package, and failure is reported rather than silently discarded.
Removal stops the services and removes the DKMS module but keeps /var/lib/vibepollo and
/etc/vibepollo. To wipe them as well, run
sudo vibepollo reset before removing the package.