Initial import: nuc and ks4 infrastructure documentation

README with instance tables and nuc<->ks4 network flow chart;
per-container install/troubleshooting docs for nuc (jellyfin
server/client, transmission-bt, bare-metal reinstall) and the
ks4 two-leg backup scheme (incus-copy over wireguard).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Julien Lutran
2026-08-09 22:50:07 +02:00
co-authored by Claude Fable 5
commit ee9eaee889
7 changed files with 1255 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
# jellyfin-server
Jellyfin media **server** in an unprivileged Incus container on `nuc`.
- Image: `images:ubuntu/24.04`, Jellyfin from the official repo (repo.jellyfin.org)
- IP: `192.168.0.5` (LAN bridge) — web UI/API on `http://192.168.0.5:8096`
- iGPU render node (`/dev/dri/renderD128`) passed for QSV/VAAPI hardware transcoding
- Media library: host `/srv/media` (ZFS dataset `usb4t/media`, USB 4 TB)
mounted at `/media` with `shift=true` (needs ZFS ≥ 2.2 for idmapped mounts)
- Port 8096 additionally proxied to the host address (`web` proxy device)
## Install script
Run as root on the Incus host: `MEDIA_DIR=/srv/media ./install.sh`
```bash
#!/usr/bin/env bash
# Adapted from:
# https://forgejo.benoit.jp.net/benoitjpnet/Laminar/src/branch/main/cfg/jobs/jellyfin.run
set -euxo pipefail
CNAME="${CNAME:-jellyfin-server}"
IMAGE="${IMAGE:-images:ubuntu/24.04}"
MEDIA_DIR="${MEDIA_DIR:-/srv/media}" # host directory containing your media library
[ -d "$MEDIA_DIR" ] || { echo "MEDIA_DIR=$MEDIA_DIR does not exist on the host"; exit 1; }
incus launch "$IMAGE" "$CNAME"
# Wait until the container has working DNS/network
for i in $(seq 1 30); do
incus exec "$CNAME" -- getent hosts repo.jellyfin.org >/dev/null 2>&1 && break
sleep 2
done
incus config set "$CNAME" environment.DEBIAN_FRONTEND=noninteractive
incus config set "$CNAME" environment.DEBCONF_NONINTERACTIVE_SEEN=true
# --- Jellyfin from the official repository -----------------------------------
incus exec "$CNAME" -- apt-get update
incus exec "$CNAME" -- apt-get upgrade -y
incus exec "$CNAME" -- apt-get install -y --no-install-recommends curl gnupg ca-certificates
incus exec "$CNAME" -- mkdir -p /etc/apt/keyrings
incus exec "$CNAME" -- bash -c 'curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/keyrings/jellyfin.gpg'
incus exec "$CNAME" -- bash -c 'cat > /etc/apt/sources.list.d/jellyfin.sources <<EOF
Types: deb
URIs: https://repo.jellyfin.org/ubuntu
Suites: noble
Components: main
Architectures: amd64
Signed-By: /etc/apt/keyrings/jellyfin.gpg
EOF'
incus exec "$CNAME" -- apt-get update
incus exec "$CNAME" -- apt-get install -y jellyfin
# Optional: OpenCL runtime, only needed for HDR tone-mapping during transcodes
incus exec "$CNAME" -- apt-get install -y --no-install-recommends intel-opencl-icd || true
# --- iGPU render node for hardware transcoding (QSV/VAAPI) -------------------
RENDER_GID="$(incus exec "$CNAME" -- getent group render | cut -d: -f3)"
incus config device add "$CNAME" igpu gpu gid="$RENDER_GID"
incus exec "$CNAME" -- usermod -aG render,video jellyfin
# --- Media library (read-only is fine unless you let Jellyfin save NFO/artwork
# next to the files) --------------------------------------------------
# NOTE: shift=true requires idmapped-mount support on MEDIA_DIR's filesystem —
# fine on local fs (ext4/btrfs/xfs), NOT supported on CIFS/NFS mounts.
incus config device add "$CNAME" media disk source="$MEDIA_DIR" path=/media shift=true
# --- Expose the web UI/API on the host's LAN address -------------------------
incus config device add "$CNAME" web proxy listen=tcp:0.0.0.0:8096 connect=tcp:127.0.0.1:8096
incus config set "$CNAME" boot.autostart=true
incus restart "$CNAME"
echo "Done. Open http://<host-ip>:8096 to run the setup wizard."
```
## First-run configuration
1. Run the setup wizard; add libraries pointing at `/media/...`.
2. Dashboard → Playback → Transcoding:
- Hardware acceleration: **Intel QuickSync (QSV)** (fallback: VA-API),
device `/dev/dri/renderD128`.
- Enable hardware decoding for the codecs you use; Alder Lake-N does
H.264/HEVC/VP9/AV1 decode and H.264/HEVC encode.
- Enable "Low-Power" encoders only if GuC/HuC is loaded (see below).
## Troubleshooting / notes
- **`Failed to setup device mount "media": idmapping abilities are required
but aren't supported on system`** — the media source is on a filesystem
without idmapped-mount support (CIFS/NFS). Either move the media to a
local fs, or drop the shift (`incus config device set jellyfin-server
media shift=false`; on CIFS files are world-readable synthetic ownership,
enough for a read-only library). Hot-applying `shift` on a running
container fails — stop it first.
- **QSV "low-power" encode fails** — Alder Lake-N needs GuC/HuC firmware
submission. On the **host**:
`echo 'options i915 enable_guc=3' > /etc/modprobe.d/i915.conf`,
`update-initramfs -u`, reboot; verify with `dmesg | grep -i 'guc\|huc'`.
Or just untick the low-power options.
- GPU check: `incus exec jellyfin-server -- ls -l /dev/dri`.
- Pin a version with `apt-get install jellyfin=<ver>+ubu2404` for
reproducibility; keep major versions in sync with the client's JMP.
- To bake a reusable image instead: run the script, then
`incus publish jellyfin-server --alias jellyfin-server-<ver>`.