Files
punktfunk/docs-site/content/docs/ubuntu.md
T
enricobuehler 62a6fa9fac
ci / bun-nix (pull_request) Successful in 29s
ci / docs-site (pull_request) Successful in 1m37s
ci / web (pull_request) Successful in 2m39s
ci / rust-arm64 (pull_request) Successful in 4m10s
ci / rust (pull_request) Successful in 6m50s
nix / flake (pull_request) Failing after 23m28s
fix(packaging): create the punktfunk group everywhere the udev rule needs it
60-punktfunk.rules chgrp's the usbip vhci attach/detach nodes to a dedicated
`punktfunk` group (security-review 2026-08-05 M-4: writing `attach` materialises
an arbitrary emulated USB device, so it must not ride on `input`). Four of the
six install paths shipped that rule in 0.25.0 without ever creating the group.
chgrp then failed, the nodes stayed root:root 0644, and the virtual Steam Deck
pad silently never attached — while `usermod -aG punktfunk` failed outright with
"group 'punktfunk' does not exist".

Affected and fixed:

  * arch  — post_upgrade() called only _ensure_update_group, so every box that
            reached 0.25.0 by `pacman -Syu` missed it; post_install was correct.
  * nix   — no users.groups.punktfunk at all, though host.users' own description
            already promised the usbip/vhci pad. Declares it now and adds
            host.users to both groups.
  * bazzite sysext — a group is host state and cannot ride an image, and the
            deb/rpm scriptlets that would create it never run there.
  * steamdeck install.sh/update.sh — handled `input` only. Both now create the
            group and join it: running that script IS the statement "make my
            Deck a host with native pad passthrough".

deb and rpm were correct throughout (one postinst/%post for install + upgrade).

Also on the Deck path: web.env secret hygiene. install.sh's `chmod 600` sat
inside the create-only branch despite a comment calling it "the idempotent belt
for a pre-existing file", and update.sh never touched the config dir at all — so
an install set up once and only updated since kept web.env world-readable
(0644) with the console password and session secret in it. Both scripts now
harden ~/.config/punktfunk to 0700 and web.env to 0600 on every run, and say so
loudly, because a chmod does not un-leak an already-readable secret: the
password still needs rotating.

Both group blocks are `if ensure_group ...` rather than `ensure_group || true`:
a failed groupadd must not fall through to a usermod against a nonexistent
group, which under `set -e` aborted install.sh after the long build and
update.sh before the service restart (verified: exit 6, no restart).

Docs: the group is now documented where people actually look — the per-distro
guides, install.md, steamos-host.md, a new troubleshooting entry for "pad
arrives as an Xbox 360 controller", and the uninstall pages. The 0.25.0 notes
gain the "group does not exist" caveat and turn the password bullet from
"consider rotating" into a real instruction, and CHANGELOG records the known
issue against the breaking change that introduced it.

Verified: bash -n on all four scripts; the arch scriptlet's post_upgrade driven
in a container (creates the group, idempotent on re-run); the ensure_group
helper and both membership branches, including a control that reproduces the
original bug (chgrp to a missing group leaves the node root:root 0644); the
find -perm /0077 probe across 0644/0640/0604/0600/0400 on GNU findutils;
`nix flake check --no-build` (the exact CI gate) and a NixOS eval showing
alice.extraGroups == ["input","punktfunk"]; docs-site build + typecheck.
2026-08-08 17:53:36 +02:00

256 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Ubuntu
description: Install the Punktfunk host on Ubuntu with apt.
---
Install a Punktfunk host on **Ubuntu** (Desktop or Server) from the apt registry. This
page covers the distro-level setup — GPU driver, package, gamepad access. It works with either GNOME
or KDE; how the host creates its virtual display and injects input is desktop-specific, so pick your
desktop on the [configure pages](#configure-your-desktop) afterward rather than here.
> New here? Read [Security & Safe Use](/docs/security) first — a streaming host is remote control of
> the machine, so keep it on a trusted LAN or VPN and require pairing.
> **Which releases.** There is one universal host package. It bundles FFmpeg 8 and needs **glibc 2.39
> or newer** — that's **Ubuntu 24.04 LTS through 26.04**, the range it's built and tested for. Check
> yours with `ldd --version`; below 2.39 the install fails and you build from source instead
> ([appendix](#appendix--build-from-source)). The desktop *client* package is built on Ubuntu 26.04
> and needs GTK4 ≥ 4.20 and SDL3, so it installs on **26.04 or newer** only — the host has no such
> limit.
> **Debian isn't a supported target.** The packages are built on Ubuntu images and their dependencies
> are resolved against Ubuntu's package names, and nothing in CI builds or tests on Debian. Debian 12
> (bookworm) is below the glibc floor and cannot install them at all. A newer Debian may work, but
> it's untested — build from source ([appendix](#appendix--build-from-source)) if you want to try.
> The `debian` in the repository URL below is the *package format*, not a supported distro.
## 1. GPU driver
On **NVIDIA**, install the recommended driver.
```sh
sudo ubuntu-drivers install # or: sudo apt install nvidia-driver-<version>
```
Then make sure the **GL/EGL userspace** is present — Wayland compositors on NVIDIA need it, and the
base driver package doesn't always pull it in. Install the `libnvidia-gl` package matching your
driver version:
```sh
sudo apt install libnvidia-gl-<version> # e.g. libnvidia-gl-550
```
Reboot, then confirm the driver and KMS modeset:
```sh
nvidia-smi
cat /sys/module/nvidia_drm/parameters/modeset # should print Y
```
If modeset is not `Y`:
```sh
echo 'options nvidia-drm modeset=1' | sudo tee /etc/modprobe.d/nvidia-drm.conf
sudo update-initramfs -u && sudo reboot
```
> **Secure Boot:** on a machine with Secure Boot **enabled**, the NVIDIA kernel module won't load
> until you enrol its signing key. If `nvidia-smi` reports it can't talk to the driver, run
> `sudo mokutil --import /var/lib/shim-signed/mok/MOK.der` (set a one-time password), reboot, and
> choose **Enrol MOK** at the blue screen. Or disable Secure Boot in firmware.
On **AMD/Intel** none of the NVIDIA steps apply. Encode runs on the Mesa stack: **Vulkan Video** for
HEVC and AV1 (`mesa-vulkan-drivers`), with **VAAPI** for H.264 and as the fallback —
`mesa-va-drivers` on AMD, `intel-media-va-driver` on Intel, both of which the `punktfunk-host`
package recommends, so apt pulls the right one in with the host. Install `mesa-vulkan-drivers` too
if it isn't already on the box.
## 2. Install the host (apt)
`punktfunk-host` is published as a `.deb` to the public Gitea apt registry, so the box installs and
updates with plain `apt`. The registry is public — no auth needed, just trust its signing key:
```sh
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://git.unom.io/api/packages/unom/debian/repository.key \
| sudo tee /etc/apt/keyrings/punktfunk.asc >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/punktfunk.asc] https://git.unom.io/api/packages/unom/debian stable main" \
| sudo tee /etc/apt/sources.list.d/punktfunk.list
sudo apt update
sudo apt install punktfunk-host
```
`punktfunk-host` `Recommends` the browser console (`punktfunk-web`), so apt pulls it in by default.
The desktop *client* (`punktfunk-client`) is a separate package for the machine you stream *to* — not
installed on a host. The NVIDIA driver is **not** a dependency — you installed it out of band in
step 1. Later updates are just `sudo apt update && sudo apt upgrade`, or, to move only Punktfunk,
`sudo apt update && sudo apt install --only-upgrade punktfunk-host`. Either way, restart the running
host afterwards so it picks up the new binary:
```sh
systemctl --user restart punktfunk-host
```
A plain `apt upgrade` also moves `punktfunk-web` when a new console is out — restart that one too
(`systemctl --user restart punktfunk-web`) if you run it.
[Updating the Host](/docs/updating) covers the rest — the web console's **Updates** card, and the
opt-in one-click update button whose `punktfunk-update` group this package creates.
The `stable` component above is the stable channel. To track pre-release builds instead, see
[Release Channels](/docs/channels).
## 3. Grant gamepad access
Virtual gamepads inject through `/dev/uinput`, which is gated by the `input` group. Add yourself and
re-login so the new group membership takes effect:
```sh
sudo usermod -aG input "$USER" # re-login to apply
```
Only if you want the **virtual Steam Deck controller** (paddles, trackpads, gyro), also join
`punktfunk`. That pad reaches games as a real USB device over usbip — which is what makes Steam
Input adopt it — and the group gating those nodes is deliberately separate from `input`, because
writing the usbip `attach` file can materialise arbitrary emulated USB hardware:
```sh
sudo usermod -aG punktfunk "$USER" # re-login to apply
```
Join it only on a machine you trust. Skip it and everything else still works; the pad just arrives
as an ordinary Xbox 360 controller.
## 4. Check it installed
Before moving on, confirm the binary is there and nothing else is competing for the same job:
```sh
punktfunk-host --version # the binary is on PATH
punktfunk-host detect-conflicts # exits 1 if Sunshine/Apollo is also installed
```
If `detect-conflicts` reports another streaming host, remove it before going further — two hosts on
one machine is the most common reason a clean install never streams. See
[Troubleshooting → another streaming host is installed](/docs/troubleshooting#another-streaming-host-sunshine-apollo--is-installed).
Once you've enabled the service on your desktop page below, these are how you watch it:
```sh
systemctl --user status punktfunk-host # active
journalctl --user -u punktfunk-host -f # watch a client connect
```
## 5. Open the firewall (if you have one)
Ubuntu installs `ufw` but leaves it **inactive**, so out of the box
there is nothing to open. If you did enable one — common on Ubuntu Server — the package ships the
openers for both, because a package never edits your firewall itself.
The packaged unit runs `serve --gamestream` — the `.deb` installs it as it ships and only rewrites
the binary path — so a host you enabled with `systemctl --user enable --now punktfunk-host` serves
**both** the native `punktfunk/1` plane and stock [Moonlight](/docs/moonlight) clients, and needs
**both** openers:
```sh
# ufw:
sudo ufw allow punktfunk-native
sudo ufw allow punktfunk-gamestream
# firewalld:
sudo firewall-cmd --reload # load the installed definitions
sudo firewall-cmd --permanent --add-service=punktfunk-native
sudo firewall-cmd --permanent --add-service=punktfunk-gamestream
sudo firewall-cmd --reload
```
Switched the host to **native-only** — dropped `--gamestream` with a
`systemctl --user edit punktfunk-host` drop-in, or you run `punktfunk-host serve` by hand? Then open
`punktfunk-native` alone and leave `punktfunk-gamestream` closed. `systemctl --user cat
punktfunk-host` shows which one yours is.
`punktfunk-native` opens UDP 9777 (QUIC control), UDP 5353 (mDNS discovery) and TCP 47990 (the
mgmt/library API — HTTPS + mTLS, read-only off loopback). `punktfunk-gamestream` opens the fixed
Moonlight ports — TCP 47984, 47989 and 48010, UDP 4799848000 — plus the same mDNS. The media
**data plane** uses an ephemeral UDP port the client opens with a hole-punch, so there is nothing
fixed to open for video.
Running the web console (`punktfunk-web`) and want to reach it from another device? Open it too —
that's **TCP 47992**:
```sh
sudo ufw allow punktfunk-web # ufw
sudo firewall-cmd --permanent --add-service=punktfunk-web && sudo firewall-cmd --reload # firewalld
```
Full port lists are in
[`packaging/debian/README.md`](https://git.unom.io/unom/punktfunk/src/branch/main/packaging/debian/README.md#firewall).
## Configure your desktop
How the host creates its virtual display and injects input depends on your desktop, not your distro.
Continue on the page for the desktop you run — it covers your `host.env`, any compositor quirks, and
starting the host:
- [KDE Plasma (KWin)](/docs/kde)
- [GNOME (Mutter)](/docs/gnome)
- [Steam / gamescope](/docs/gamescope)
- [Hyprland](/docs/hyprland)
- [Sway / wlroots](/docs/sway)
Then bring up [The Web Console](/docs/web-console) to arm pairing and connect your first
[client](/docs/clients). To run the host at boot — including fully **headless** — see
[Running as a Service](/docs/running-as-a-service).
## Next steps
- **Keep it current** — [Updating the Host](/docs/updating).
- **Remove it again** — [Uninstalling](/docs/uninstall).
- **Something not working?** — [Troubleshooting](/docs/troubleshooting).
## Appendix — build from source
If your release is older than the supported range above, or you want to track `main` directly,
compile the host yourself (no clean updates / no packaged units — you wire those up by hand).
Install the build toolchain and runtime libraries. The packaged host is built against **FFmpeg 8**:
Ubuntu 26.04's `libavcodec-dev` is FFmpeg 8, but 24.04's is FFmpeg 6.1 — on 24.04 build FFmpeg 8
yourself first (that's what `ci/rust-ci-noble.Dockerfile` does, and the shipped `.deb` bundles the
result) or stick with the packaged host.
```sh
sudo apt install build-essential pkg-config cmake clang libclang-dev nasm git curl \
pipewire pipewire-pulse wireplumber libpipewire-0.3-dev libspa-0.2-dev \
libwayland-dev wayland-protocols libxkbcommon-dev libopus-dev \
libdrm-dev libgbm-dev libgl-dev libegl-dev libgles-dev mesa-common-dev libva-dev \
ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev libavdevice-dev \
libnvidia-egl-wayland1 libnvidia-egl-gbm1 libei-dev
```
Install Rust if you don't have it, then build:
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://git.unom.io/unom/punktfunk.git && cd punktfunk
cargo build --release --locked \
--features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
-p punktfunk-host
```
Those two features are what the packaged build uses — without them the host has no direct NVENC
(NVIDIA) and no Vulkan Video encode (AMD/Intel), and falls back to the slower libav backends.
The host binary lands at `target/release/punktfunk-host`. Configure your desktop as above, then run
it from inside your session:
```sh
cargo run --release --locked \
--features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
-p punktfunk-host -- serve --gamestream
```
(The native plane is always on; `--gamestream` adds the Moonlight-compat surface — trusted LAN only.
Drop it for a secure native-only host.)