quadlet: drop the userns identity model — rootless cannot map uid 997
Evidence from the failed first cutover: podman top shows container root maps to dank (1000) under UserNS=host rootless (subuid range 100000:65536 cannot express 997), so PUID=997 fell back to root and PMS crash-looped on write access to its 997:984-owned data root. Fleet model instead: PUID=0/ PGID=0 (container root = dank, same as jellyfin/copyparty data roots) and deploy_plex chowns /var/lib/plexmediaserver to 1000:1000 inside the first-cutover block. Zero bytes moved; rollback regains one chown -R 997:984 step, documented in Plex.md. Seam test pins PUID=0/PGID=0 and the absence of any userns key.
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
[Unit]
|
||||
Description=Plex Media Server (quadlet, userns=host)
|
||||
Description=Plex Media Server (quadlet, rootless fleet identity)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
Image=lscr.io/linuxserver/plex:1.43.3.10896-cb3ebc72d-ls322
|
||||
ContainerName=plex
|
||||
UserNS=host
|
||||
Environment=PUID=997
|
||||
Environment=PGID=984
|
||||
Environment=PUID=0
|
||||
Environment=PGID=0
|
||||
Environment=TZ=America/Los_Angeles
|
||||
Volume=/var/lib/plexmediaserver:/config
|
||||
Volume=/mnt/entropy:/mnt/entropy:ro
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
[Unit]
|
||||
Description=Tautulli (quadlet, userns=host)
|
||||
Description=Tautulli (quadlet, rootless fleet identity)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
Image=lscr.io/linuxserver/tautulli:v2.18.1-ls242
|
||||
UserNS=host
|
||||
Network=dankserver_appnet:alias=tautulli
|
||||
Environment=PUID=1000
|
||||
Environment=PGID=1000
|
||||
Environment=PUID=0
|
||||
Environment=PGID=0
|
||||
Environment=TZ=America/Los_Angeles
|
||||
Volume=/home/dank/Containers/tautulli:/config
|
||||
PublishPort=8181:8181
|
||||
|
||||
@@ -672,10 +672,15 @@ deploy_plex() {
|
||||
if ! systemctl --user is-active -q plex.service; then
|
||||
# First cutover: retire the native package unit BEFORE starting the
|
||||
# container — both cannot own 32400. Mask (not disable): on these hosts
|
||||
# `systemctl disable` deletes the unit file itself; mask keeps an
|
||||
# instant byte-safe rollback while nothing is signed off yet.
|
||||
# `systemctl disable` deletes the unit file itself; mask keeps a
|
||||
# one-command rollback while nothing is signed off yet.
|
||||
run_privileged systemctl stop plexmediaserver.service
|
||||
run_privileged systemctl mask plexmediaserver.service
|
||||
# Rootless identity model: container root maps to the dank user
|
||||
# (uid 1000) — real uid 997 is not mappable rootless. Adopt the data
|
||||
# root into the fleet convention (jellyfin et al. run as 1000:1000).
|
||||
# Rollback chowns it back to 997:984 before restarting the native unit.
|
||||
run_privileged chown -R 1000:1000 /var/lib/plexmediaserver
|
||||
fi
|
||||
systemctl --user restart plex.service
|
||||
# Readiness gate: /identity answers MediaContainer XML once PMS is up;
|
||||
|
||||
@@ -18,7 +18,7 @@ public traffic can be fronted by Caddy like every other vhost.
|
||||
| Image | `lscr.io/linuxserver/plex:1.43.3.10896-cb3ebc72d-ls322` (pinned; verified == the ghcr `latest` manifest digest at pin time) |
|
||||
| Data root | `/var/lib/plexmediaserver` bind-mounted **in place** as `/config` (RW) — the migration moved zero bytes |
|
||||
| Media | `/mnt/entropy` mounted at the same absolute path **read-only** — every library path in the PMS database stayed valid, zero re-scan |
|
||||
| Identity | `UserNS=host` (quadlet key for `--userns=host`) + `PUID=997 PGID=984` — container writes land owned 997:984, byte-compatible with the former native unit |
|
||||
| Identity | fleet rootless model — container root maps to the `dank` user (1000:1000); the deployer chowns the data root on first cutover |
|
||||
| Port | `32400/tcp` published on `0.0.0.0` — zero client reconfiguration |
|
||||
| Network | `dankserver_appnet`, alias `plex`; Tautulli reaches it at `http://plex:32400` |
|
||||
| GPU | none by decision — CPU decode only; the RTX 3090 stays dedicated to `frank-default-small` |
|
||||
@@ -30,18 +30,21 @@ public traffic can be fronted by Caddy like every other vhost.
|
||||
The migration moved zero bytes: the container bind-mounts the native data
|
||||
root where it already lived, and the media archive re-mounts at
|
||||
`/mnt/entropy` inside the container, so every absolute library path in the
|
||||
PMS database keeps resolving. `UsernsMode=host` + `PUID=997/PGID=984` makes
|
||||
the container's file identity indistinguishable from the old service's —
|
||||
no chown, no re-scan. Rollback is byte-safe at any time before decommission.
|
||||
PMS database keeps resolving. The one metadata change: rootless podman
|
||||
cannot map the real host uid 997, so the fleet model applies — container
|
||||
root runs as the `dank` user, and the deployer adopts the data root with
|
||||
`chown -R 1000:1000` on first cutover (rollback chowns it back).
|
||||
|
||||
## Cutover & rollback
|
||||
|
||||
- First deploy stops and **masks** `plexmediaserver.service` (never
|
||||
`disable` — on these hosts `disable` deletes the unit file), then starts
|
||||
the quadlet; the deployer blocks until `/identity` answers on 32400.
|
||||
`disable` — on these hosts `disable` deletes the unit file), chowns
|
||||
`/var/lib/plexmediaserver` to `1000:1000`, then starts the quadlet; the
|
||||
deployer blocks until `/identity` answers on 32400.
|
||||
- Rollback lever while masked: `systemctl --user stop plex.service && sudo
|
||||
systemctl unmask plexmediaserver.service && sudo systemctl start
|
||||
plexmediaserver.service`.
|
||||
chown -R 997:984 /var/lib/plexmediaserver && sudo systemctl unmask
|
||||
plexmediaserver.service && sudo systemctl start plexmediaserver.service`
|
||||
(ownership-only churn — zero bytes ever moved).
|
||||
- Decommission (`sudo apt-get -y purge plexmediaserver`) only after explicit
|
||||
operator sign-off.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ it; it talks to PMS over the appnet at `http://plex:32400`).
|
||||
| Runtime | podman quadlet, `tautulli.service` (user manager) — migrated from snap `snap.tautulli.tautulli.service` |
|
||||
| Image | `lscr.io/linuxserver/tautulli:v2.18.1-ls242` (pinned; verified == the ghcr `latest` manifest digest at pin time) |
|
||||
| Data root | `/home/dank/Containers/tautulli` bind-mounted as `/config` (fresh at cutover — the snap's `/var/snap/tautulli/common` was empty) |
|
||||
| Identity | `UserNS=host` (quadlet key for `--userns=host`) + `PUID=1000 PGID=1000` |
|
||||
| Identity | fleet rootless model — container root maps to the `dank` user (1000:1000) |
|
||||
| Port | `8181/tcp` published on `0.0.0.0` |
|
||||
| Network | `dankserver_appnet`, alias `tautulli` |
|
||||
| PMS URL | `http://plex:32400` — seeded into `config.ini` by the deployer on first start |
|
||||
|
||||
@@ -67,14 +67,16 @@ load helpers
|
||||
grep -q '^PublishPort=8181:8181$' "$REPO_ROOT/dankserver/tautulli/quadlet/tautulli.container" || return 1
|
||||
}
|
||||
|
||||
@test "plex keeps the native unit's identity (UserNS=host, PUID=997, PGID=984)" {
|
||||
# Zero-chown ownership contract: /var/lib/plexmediaserver is bind-mounted
|
||||
# in place, so every file the container writes must land owned 997:984 —
|
||||
# byte-identical to the former plexmediaserver.service identity. UserNS=
|
||||
# is the quadlet v1 key (UsernsMode= is compose syntax and the generator
|
||||
# hard-fails the whole unit on it); pin the exact keys.
|
||||
@test "plex runs the fleet identity model (PUID=0/PGID=0, no userns remap)" {
|
||||
# Rootless podman cannot map the real host uid 997 (subuid range is
|
||||
# 100000:65536), so the fleet model applies: container root maps to the
|
||||
# dank user and the deployer chowns /var/lib/plexmediaserver to
|
||||
# 1000:1000 on first cutover; rollback chowns back to 997:984. Pin both
|
||||
# keys and the ABSENCE of any userns key — UsernsMode= is compose syntax
|
||||
# (the v1 generator hard-fails the whole unit on it) and UserNS= would
|
||||
# remap the container away from the fleet identity.
|
||||
unit="$REPO_ROOT/dankserver/plex/quadlet/plex.container"
|
||||
grep -q '^UserNS=host$' "$unit" || return 1
|
||||
grep -q '^Environment=PUID=997$' "$unit" || return 1
|
||||
grep -q '^Environment=PGID=984$' "$unit" || return 1
|
||||
grep -q '^Environment=PUID=0$' "$unit" || return 1
|
||||
grep -q '^Environment=PGID=0$' "$unit" || return 1
|
||||
! grep -qE '^(UserNS|UsernsMode)=' "$unit" || return 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user