Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae6c81bf54 | |||
| f01b1f2c4f | |||
| ff34fe762e | |||
| 7e4407a1f8 | |||
| 539fb26791 | |||
| 003a2f77dd | |||
| 86e7f9c1a8 | |||
| adb7fcfad7 | |||
| 79b26ddfda | |||
| b8961fb47d | |||
| 4851f745d8 |
@@ -106,3 +106,40 @@ show up as low CPU, some GPU (`intel_gpu_top`) activity instead.
|
||||
- Exact supported codec list depends on the iGPU generation (device ID
|
||||
`9a49`) — confirm via `vainfo` output once run, and enable only the
|
||||
hardware decode paths it actually reports.
|
||||
|
||||
## Post-deploy fix: two additional runtime packages required
|
||||
|
||||
After the initial deploy (Task 1's `SupplementaryGroups` grant) and
|
||||
enabling QSV in the dashboard, HEVC HDR playback hung indefinitely
|
||||
(Direct Play worked for some titles; titles that needed a real
|
||||
transcode+tonemap never produced output). Root-caused via
|
||||
`journalctl -u jellyfin` and the per-session ffmpeg transcode log
|
||||
(`find / -xdev -iname '*ffmpeg-transcode*'`) — two separate runtimes
|
||||
were missing beyond `intel-media-driver` (which only provides VAAPI):
|
||||
|
||||
1. **QSV session creation failed:** `Error creating a MFX session: -9`
|
||||
/ `Error initializing an MFX session: -3` on
|
||||
`-init_hw_device qsv=qs@va`. VAAPI and QSV are separate runtimes on
|
||||
Linux — QSV needs the oneVPL/MFX GPU implementation. Fix: added
|
||||
`pkgs.vpl-gpu-rt` ("oneAPI Video Processing Library Intel GPU
|
||||
implementation"; note `onevpl-intel-gpu` is the old, renamed
|
||||
attribute) to `hardware.graphics.extraPackages` in
|
||||
`machines/jupiter/hardware-configuration.nix`.
|
||||
|
||||
2. **OpenCL device creation failed:** `Failed to get number of OpenCL
|
||||
platforms: -1001` (`CL_PLATFORM_NOT_FOUND_KHR`) on
|
||||
`-init_hw_device opencl=ocl@va`. The `tonemap_opencl` filter jellyfin
|
||||
uses for HDR→SDR tone-mapping needs a working OpenCL ICD, which
|
||||
nothing installed so far provides. Fix: added
|
||||
`pkgs.intel-compute-runtime` ("Intel Graphics Compute Runtime oneAPI
|
||||
Level Zero and OpenCL, supporting 12th Gen and newer" — matches
|
||||
jupiter's Tiger Lake/Xe iGPU) to the same `extraPackages` list.
|
||||
|
||||
Confirmed working end-to-end: HEVC HDR transcode with QSV encode +
|
||||
OpenCL tone-map runs at `speed=2.68x` realtime on jupiter's iGPU, and
|
||||
plays smoothly on Apple TV (JellyTV app).
|
||||
|
||||
Both packages live in `machines/jupiter/hardware-configuration.nix`
|
||||
(`hardware.graphics.extraPackages`), alongside `intel-media-driver`,
|
||||
rather than in the jellyfin module itself — they're iGPU runtime
|
||||
capabilities, not something specific to the jellyfin service.
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# mibook: choose terminal-only vs desktop at boot
|
||||
|
||||
## Goal
|
||||
|
||||
Let mibook offer a choice, each time it boots, between the normal KDE desktop
|
||||
and a terminal-only ("server") mode with no graphical session — decided at
|
||||
boot, without rebuilding the system.
|
||||
|
||||
## Background: why the first attempt failed
|
||||
|
||||
The first implementation booted mibook to a text console by default
|
||||
(`systemd.defaultUnit = "multi-user.target"`, display manager not started) and
|
||||
provided a `desktop` command to start KDE on demand. On real hardware this
|
||||
locked the machine out:
|
||||
|
||||
- **No SSH.** mibook is WiFi-only and its WiFi credentials are stored per-user
|
||||
in KWallet ("agent-owned"). NetworkManager only receives the password once a
|
||||
desktop session is running, so a headless boot never joins the network and
|
||||
the machine has no IP — nothing to SSH into.
|
||||
- **No usable console.** The boot appeared to "hang" with no login prompt:
|
||||
`NetworkManager-wait-online` stalled waiting for a network that never came
|
||||
up, and the `getty` login prompt on tty1 was buried under later service
|
||||
messages.
|
||||
|
||||
Conclusion: a headless WiFi laptop cannot be reached remotely, and the plain
|
||||
console was hard to use. The design must (a) keep the desktop as the reliable
|
||||
default, (b) make the terminal path a deliberate, self-sufficient choice, and
|
||||
(c) not depend on the network being up.
|
||||
|
||||
## Behavior
|
||||
|
||||
- The **default GRUB entry** boots straight into KDE — unchanged from the
|
||||
known-working baseline.
|
||||
- A **separate GRUB entry, `mibook (terminal)`** (a NixOS *specialisation*),
|
||||
boots to a text console with **autologin** for `finn`. From there the user
|
||||
can work in the shell or run `desktop` to bring KDE up (via SDDM).
|
||||
- The choice is made in the GRUB menu at boot — matching the original request
|
||||
to "decide each time I boot."
|
||||
- Booting never stalls on the network.
|
||||
|
||||
### Known limitation (documented, not fixed in config)
|
||||
|
||||
In terminal mode WiFi will not connect on its own, because the password is
|
||||
stored per-user in KWallet. To reach mibook over SSH from terminal mode, the
|
||||
user must first save the WiFi as a **system** connection in KDE:
|
||||
network settings → the WiFi network → "All users may connect to this network".
|
||||
Until then, terminal mode is local-console-only. This is a one-time manual
|
||||
step outside the scope of the Nix config.
|
||||
|
||||
## Implementation
|
||||
|
||||
### `machines/mibook/configuration.nix`
|
||||
|
||||
- Add a NixOS specialisation `specialisation.terminal.configuration`:
|
||||
- `system.nixos.tags = [ "terminal" ];` — labels the generated boot entry.
|
||||
- `systemd.defaultUnit = lib.mkForce "multi-user.target";` — boots to the
|
||||
text console. `graphical.target` is what pulls in the display manager (via
|
||||
its embedded `Wants=display-manager.service`), so defaulting to
|
||||
`multi-user.target` leaves SDDM installed but not started at boot.
|
||||
- `services.getty.autologinUser = "finn";` — guarantees a usable shell on
|
||||
the console instead of a login prompt that can scroll off screen.
|
||||
- A `desktop` command via
|
||||
`pkgs.writeShellScriptBin "desktop" "exec sudo systemctl start display-manager.service"`
|
||||
in `environment.systemPackages`, to start KDE on demand.
|
||||
- Add `systemd.services.NetworkManager-wait-online.enable = false;` (applies to
|
||||
both the default and terminal boots) so boot never stalls waiting for the
|
||||
network.
|
||||
|
||||
### Reverted from the first attempt
|
||||
|
||||
- `modules/environments/kde-desktop/default.nix` — remove the `startOnBoot`
|
||||
option and its `mkMerge`/`mkIf` machinery; back to the original profile that
|
||||
simply enables SDDM + Plasma 6.
|
||||
- `machines/mibook/environments.nix` — remove `kde-desktop.startOnBoot = false;`
|
||||
(back to just `kde-desktop.enable = true;`).
|
||||
|
||||
## Why a specialisation
|
||||
|
||||
A specialisation generates a second boot-menu entry automatically from a
|
||||
modified copy of the configuration. It is the idiomatic NixOS mechanism for a
|
||||
boot-time choice and avoids fragile hand-written GRUB `extraEntries` that would
|
||||
need to track kernel/initrd paths across generations. The default entry remains
|
||||
byte-for-byte the working desktop configuration.
|
||||
|
||||
## Testing / verification
|
||||
|
||||
- `nix build '.#nixosConfigurations.mibook.config.system.build.toplevel'`
|
||||
builds both `nixos-system-mibook` and `nixos-system-mibook-terminal`.
|
||||
- Verified on the built closures:
|
||||
- Parent `default.target` → `graphical.target`; no console autologin
|
||||
(identical to the pre-change baseline).
|
||||
- Specialisation `default.target` → `multi-user.target`; tty1 getty wrapper
|
||||
contains `--autologin finn`; `desktop` present in the system profile.
|
||||
- `NetworkManager-wait-online` disabled in both.
|
||||
- Post-`switch` manual check on mibook: default GRUB entry boots to KDE; the
|
||||
`terminal` entry boots to an autologged-in console; running `desktop` there
|
||||
starts SDDM and a working Plasma session.
|
||||
|
||||
## Trade-offs
|
||||
|
||||
- Autologin on the terminal console means physical access grants a shell
|
||||
without a password. Acceptable for a personal laptop the user controls; the
|
||||
desktop (default) boot is unaffected.
|
||||
- `desktop` relies on `sudo`; the user has sudo access, so no extra config is
|
||||
required.
|
||||
Generated
+14
-14
@@ -89,11 +89,11 @@
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1784375420,
|
||||
"narHash": "sha256-dt1zVj4MRpmffhsgQk+3tx1m/pFTXbtVWrn5KZ1y+8Q=",
|
||||
"lastModified": 1784762557,
|
||||
"narHash": "sha256-R/r6jRnANV50c8F5Fz5+1Q1moab0IGWRk+cg5ME2nMY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nix",
|
||||
"rev": "bebd2f851a304e9fb2e143ce0cbeff577c6a37ac",
|
||||
"rev": "d10c84cd0cc0efdcb29cf2611caf5fbcd10fa071",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -143,11 +143,11 @@
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1784310968,
|
||||
"narHash": "sha256-rkSPTePrKqs4dg+i7ZFCq93+HrClac6oSwXX927SVjA=",
|
||||
"lastModified": 1784723954,
|
||||
"narHash": "sha256-1CfD8ZUjCkTgjsneLZ/lxCHhgDfqxxE7/GX0MmsgiqA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "779c32a00155994c86cde8213a8dd4df139d4355",
|
||||
"rev": "a017f5b72210026af5b3ac5949f08d94380a6fbd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -218,11 +218,11 @@
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1784497964,
|
||||
"narHash": "sha256-WhdsTtaih3DgTPP/PX023b36UNQyMzEoi6GkPyGx0y4=",
|
||||
"rev": "241313f4e8e508cb9b13278c2b0fa25b9ca27163",
|
||||
"lastModified": 1784796856,
|
||||
"narHash": "sha256-vwxWgF+Gj276WznzGb1LxGsK/39HaQwgQXiU3EkC844=",
|
||||
"rev": "e2587caef70cea85dd97d7daab492899902dbf5d",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1037713.241313f4e8e5/nixexprs.tar.xz"
|
||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1040357.e2587caef70c/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
@@ -245,11 +245,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1784432872,
|
||||
"narHash": "sha256-IX7j5TvSD7hYC9NZ8mgmq7pZ97mYyl24Qs4Tz4uyvO4=",
|
||||
"rev": "fd1462031fdee08f65fd0b4c6b64e22239a77870",
|
||||
"lastModified": 1784707089,
|
||||
"narHash": "sha256-DUedXhD2Rg8q4Xyd07Sb90eZGy4gg6W+Vl/WbLNwAZo=",
|
||||
"rev": "b3fe9581c9061c749abef42b6d4ee7b7c05c33fa",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/26.05/nixos-26.05.5591.fd1462031fde/nixexprs.tar.xz"
|
||||
"url": "https://releases.nixos.org/nixos/26.05/nixos-26.05.5845.b3fe9581c906/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)
|
||||
libva-vdpau-driver
|
||||
libvdpau-va-gl
|
||||
vpl-gpu-rt # oneVPL/MFX runtime, required for QSV (h264_qsv/hevc_qsv) session creation
|
||||
intel-compute-runtime # OpenCL runtime, required for tonemap_opencl (HDR tone-mapping)
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
useOSProber = true;
|
||||
};
|
||||
|
||||
|
||||
# Configure keymap in X11
|
||||
services.xserver.xkb = {
|
||||
layout = "de";
|
||||
@@ -34,7 +33,6 @@
|
||||
services.printing.enable = true;
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
|
||||
hardware.nvidia.prime = {
|
||||
sync.enable = false;
|
||||
|
||||
@@ -44,6 +42,45 @@
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
# Don't let boot stall waiting for a network that may never come up
|
||||
# (WiFi credentials live in KWallet and need a desktop session), which
|
||||
# otherwise hangs the terminal boot before the login prompt appears.
|
||||
systemd.services.NetworkManager-wait-online.enable = false;
|
||||
|
||||
# Boot-time choice: the default GRUB entry boots straight into KDE.
|
||||
# A separate "terminal" entry (a NixOS specialisation) boots to a text
|
||||
# console with autologin, where you can work or run `desktop` to bring
|
||||
# KDE up. Pick the entry you want in the GRUB menu at boot.
|
||||
#
|
||||
# NOTE: in terminal mode WiFi will not connect on its own (the password
|
||||
# is stored per-user in KWallet). To reach the machine over SSH from
|
||||
# terminal mode, first save the WiFi as a system connection in KDE:
|
||||
# network settings -> your WiFi -> "All users may connect to this network".
|
||||
specialisation.terminal.configuration = {
|
||||
system.nixos.tags = [ "terminal" ];
|
||||
|
||||
# Boot to a text console. graphical.target is what pulls in the display
|
||||
# manager (via its embedded Wants=display-manager.service), so defaulting
|
||||
# to multi-user.target leaves SDDM installed but not started at boot.
|
||||
systemd.defaultUnit = lib.mkForce "multi-user.target";
|
||||
|
||||
# Guarantee a usable shell on the console (no login prompt to hunt for).
|
||||
services.getty.autologinUser = "finn";
|
||||
|
||||
# Bring the desktop up on demand from the terminal.
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "desktop" "exec sudo systemctl start display-manager.service")
|
||||
];
|
||||
};
|
||||
|
||||
# KDE (PowerDevil) power settings: do nothing on lid close while on AC power.
|
||||
# Shipped as a system-wide default; KConfig cascades so a user's own
|
||||
# ~/.config/powerdevilrc will override this if present.
|
||||
environment.etc."xdg/powerdevilrc".text = ''
|
||||
[AC][SuspendAndShutdown]
|
||||
LidAction=0
|
||||
'';
|
||||
|
||||
system = {
|
||||
stateVersion = "23.05";
|
||||
autoUpgrade.enable = true;
|
||||
|
||||
@@ -24,4 +24,4 @@ in
|
||||
numix-icon-theme
|
||||
];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user