Mike 2026-08-20, revising the 2026-07-31 iGPU-only call with the 2026-08-04 finding that this UHD 630 has no HEVC encode entrypoint. runtime: nvidia + NVIDIA_VISIBLE_DEVICES/CAPABILITIES on the plex service; /dev/dri stays. Also brings the compose current with the live opt-in watchtower config, and 50-plex.sh now sources WATCHTOWER_NOTIFICATION_URL (carries the publish token) from stacks.env, soft-gates on nvidia-smi, and adds the NVENC line to the walk-away checks.
mediabox-bootstrap
Everything the media box (10.0.1.20, hostname mediabox) needs after the
Ubuntu 24.04 autoinstall has laid down the base system.
The autoinstall USB is deliberately thin: sshd, a key, DHCP, Docker CE, /srv,
and a first-boot unit that clones this repo and runs bootstrap.sh. That
split exists so the interesting parts stay in git, reviewable and re-runnable,
instead of frozen onto a USB stick that nobody can diff.
Full context, decisions and the phase-by-phase runbook live in the workspace repo
at projects/mediabox-linux/.
Layout
bootstrap.sh orchestrator — runs the stages, fails soft
scripts/
00-preflight.sh asserts hardware/BIOS state. Changes nothing.
10-secrets.sh idempotent ADD-ONLY seeder for /srv/secrets/stacks.env
20-cifs.sh the 6 NAS mounts, named exactly as the DB stores them
30-nvidia.sh NVIDIA 580 + container toolkit
40-shell-mcp.sh builds and starts the native shell-mcp
50-plex.sh Plex against the MIGRATED data dir. No claim token.
60-media-relocate.sh optional, post-soak: move the 337 GB preview cache
migration/ remap.sql, migrate-db.sh, gen-preferences.ps1, README.md
shell-mcp/ amd64 build of the MCP server (server.py, Dockerfile,
docker-compose.yml, pinned requirements.txt)
plex/docker-compose.yml Plex service definition
secrets/stacks.env.example placeholder master env
Running it
sudo /srv/mediabox-bootstrap/bootstrap.sh # all stages
sudo /srv/mediabox-bootstrap/scripts/20-cifs.sh # one stage
sudo /srv/mediabox-bootstrap/scripts/20-cifs.sh --verify
Every stage is idempotent. Re-running is the normal way to use this, not an emergency measure.
Design rules
Nothing may leave the box unreachable. sshd is up before any of this runs
and no stage may compromise that. bootstrap.sh catches stage failures and
continues; the systemd unit declares SuccessExitStatus=0 1 so a bad stage can
never wedge boot. There is no keyboard attached to this machine.
Secrets never ride on removable media. The CIFS credentials file is created empty by the autoinstall and filled in post-boot over the MCP.
No claim token, ever. The migration preserves MachineIdentifier, so this
is the existing server rather than a new one. Claiming would discard shared
users and every client registration.
One secrets file per host. This box has its own /srv/secrets/stacks.env,
using the same [OPERATOR] / [VALUES] / [MANIFEST] tiering as arrsstack, but
it is not shared or mounted from anywhere. Canonical copies live in Vaultwarden.
The seeder only ever adds. 10-secrets.sh will never overwrite an existing
key, change its value, or move it. If KEY= is present it is skipped entirely.
Verified against a pre-populated file: existing values, unrelated keys, the
operator tier and existing manifest lines all survive byte-for-byte, and a second
run is a no-op.
Notes that will save you an evening
shell-mcp was originally built on arm64. It builds clean on amd64 — the whole Python dependency tree resolves to prebuilt manylinux x86_64 wheels, so no compiler is needed. Two real changes were required:
- Dependencies are now pinned. The original installed
mcp starlette uvicornunpinned.starlettehas since gone 1.x. Unpinned installs are tolerable in a hand-run build and dangerous inside a first-boot script. - Port and description. It listens on 8103 (not 8085) so NPM proxy host 42 can simply be repointed, and the tool description says media box, not arrsstack — otherwise every session starts with the wrong idea of which host it is touching.
The GTX 1070 is Pascal, and NVIDIA branch 580 is the last one that supports it.
There will be no 590 for this card. 30-nvidia.sh installs 580 explicitly and pins
against newer branches. Never use ubuntu-drivers autoinstall here — it will
cheerfully install a branch that drops the card. And it must be the proprietary
module, not -open: the open kernel modules need Turing or newer.
Secure Boot must stay off. It is off today. A BIOS update resets ASUS defaults
and turns it back on, at which point the DKMS module needs interactive MOK
enrollment at a console this box does not have. 00-preflight.sh checks for this.
nofail on every CIFS mount is not optional. Without it, an unreachable NAS
drops a headless box to an emergency shell waiting for a root password on a
keyboard that is not plugged in.
autoEmptyTrash is the most dangerous default here. The NAS account is
read-write. If a mount is missing when Plex scans, Plex sees an empty library and
will act on that. 50-plex.sh disables it before any library is added.
Plex's PUID/PGID must equal the CIFS uid=/gid=. Both are 3000 (media).
If they ever drift, every file is permission-denied.