Autoinstall lays down a thin base (sshd, key, DHCP, Docker CE, /srv) and hands
off to this repo on first boot. Everything interesting stays in git so it is
reviewable and re-runnable, rather than frozen onto a USB nobody can diff.
Stages, all idempotent:
00-preflight asserts hardware/BIOS state, changes nothing. Catches a BIOS
update having silently re-enabled Secure Boot, which would stop
the NVIDIA DKMS module loading on a box with no keyboard.
10-secrets ADD-ONLY seeder for /srv/secrets/stacks.env. Never overwrites an
existing key. Verified against a pre-populated file: existing
values, unrelated keys, the operator tier and existing manifest
lines all survive byte-for-byte; a second run is a no-op.
20-cifs the 8 shares Plex actually uses (Share is excluded, it is not a
library root). \040 escaping, nofail + x-systemd.automount +
_netdev. Managed-block rewrite verified not to duplicate or to
drop the root fstab entry.
30-nvidia nvidia-driver-580 explicitly: 580 is the LAST branch supporting
Pascal, and the -open modules need Turing+. Pins against newer
branches. Not in late-commands because DKMS needs the installed
kernel, not the installer's.
40-shell-mcp builds the native MCP locally for amd64; refuses to finish
unless /sse returns 401 without a token.
50-plex run by hand: PLEX_CLAIM expires in 4 minutes. Refuses to start
against missing mounts and disables autoEmptyTrash, which with
read-write NAS credentials is the most dangerous default here.
shell-mcp was built on arm64 originally. It builds clean on amd64 (whole dep
tree resolves to prebuilt manylinux x86_64 wheels, no compiler needed), but
dependencies are now pinned - the original installed mcp/starlette/uvicorn
unpinned and starlette has since gone 1.x. Port moved to 8103 so NPM host 42
can simply be repointed, and the tool description now says media box rather
than arrsstack.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
103 lines
4.6 KiB
Markdown
103 lines
4.6 KiB
Markdown
# 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 8 NAS mounts (--verify mode included)
|
|
30-nvidia.sh NVIDIA 580 + container toolkit
|
|
40-shell-mcp.sh builds and starts the native shell-mcp
|
|
50-plex.sh Plex. Run BY HAND — needs a live claim token.
|
|
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
|
|
|
|
```bash
|
|
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. `PLEX_CLAIM`
|
|
tokens expire in four minutes and are passed as a one-shot environment variable,
|
|
never written to disk or committed.
|
|
|
|
**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 uvicorn`
|
|
unpinned. `starlette` has 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.
|