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>
94 lines
3.7 KiB
Bash
Executable File
94 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# 00-preflight — assert the box is what we think it is before changing it.
|
|
#
|
|
# This stage NEVER modifies anything. It only reports. Its job is to catch
|
|
# "the BIOS update reset your settings" before you spend an hour wondering
|
|
# why Plex will not transcode.
|
|
# =============================================================================
|
|
set -uo pipefail
|
|
|
|
RC=0
|
|
note() { printf ' %-34s %s\n' "$1" "$2"; }
|
|
bad() { printf ' \033[1;31m%-34s %s\033[0m\n' "$1" "$2"; RC=1; }
|
|
warn() { printf ' \033[1;33m%-34s %s\033[0m\n' "$1" "$2"; }
|
|
|
|
echo "--- identity ---"
|
|
note "hostname" "$(hostname)"
|
|
note "kernel" "$(uname -r)"
|
|
note "ip" "$(hostname -I | tr -s ' ')"
|
|
|
|
echo "--- firmware ---"
|
|
if [ -d /sys/firmware/efi ]; then note "boot mode" "UEFI"; else bad "boot mode" "LEGACY/BIOS — expected UEFI"; fi
|
|
|
|
# Secure Boot must stay OFF. A BIOS update commonly restores defaults, and the
|
|
# ASUS default for this board turns Secure Boot back on. With it on, the DKMS
|
|
# NVIDIA module will not load and there is no keyboard attached to enroll a MOK.
|
|
if command -v mokutil >/dev/null 2>&1; then
|
|
SB="$(mokutil --sb-state 2>/dev/null || echo unknown)"
|
|
case "$SB" in
|
|
*disabled*) note "secure boot" "disabled (correct)" ;;
|
|
*enabled*) bad "secure boot" "ENABLED — NVIDIA DKMS will not load. Disable it in BIOS." ;;
|
|
*) warn "secure boot" "$SB" ;;
|
|
esac
|
|
else
|
|
warn "secure boot" "mokutil not installed; check BIOS manually"
|
|
fi
|
|
|
|
echo "--- disks ---"
|
|
# Disk 0 must be the system disk. Disk 1 must still be NTFS and untouched.
|
|
SYS_SERIAL="$(lsblk -dno SERIAL "$(findmnt -no SOURCE / | sed -E 's/p?[0-9]+$//')" 2>/dev/null | tr -d ' ')"
|
|
if [ "$SYS_SERIAL" = "1808AE802176" ]; then
|
|
note "root disk serial" "1808AE802176 (correct)"
|
|
else
|
|
bad "root disk serial" "${SYS_SERIAL:-unknown} — expected 1808AE802176"
|
|
fi
|
|
|
|
DATA_DEV="$(lsblk -dno NAME,SERIAL | awk '$2=="WD-WCC7K3JAEDR3"{print $1}')"
|
|
if [ -n "$DATA_DEV" ]; then
|
|
FSTYPES="$(lsblk -no FSTYPE "/dev/$DATA_DEV" | tr -s '\n' ' ')"
|
|
note "data disk (D:)" "/dev/$DATA_DEV present, fstypes: ${FSTYPES:-none}"
|
|
if echo "$FSTYPES" | grep -q ntfs; then
|
|
note "data disk state" "still NTFS — correct, leave it until after soak"
|
|
else
|
|
warn "data disk state" "no NTFS found — has it already been converted?"
|
|
fi
|
|
if findmnt -rno TARGET -S "/dev/${DATA_DEV}1" >/dev/null 2>&1; then
|
|
warn "data disk mounted" "D: is mounted; it should not be during the soak"
|
|
fi
|
|
else
|
|
bad "data disk (D:)" "WD-WCC7K3JAEDR3 NOT FOUND"
|
|
fi
|
|
|
|
echo "--- gpu ---"
|
|
if lspci -nn | grep -qi 'VGA.*Intel'; then
|
|
note "iGPU (UHD 630)" "present"
|
|
else
|
|
bad "iGPU (UHD 630)" "NOT enumerated — set BIOS: Advanced > System Agent (SA) Configuration > Graphics Configuration > iGPU Multi-Monitor = Enabled"
|
|
fi
|
|
if lspci -nn | grep -qi 'NVIDIA'; then
|
|
note "GTX 1070" "$(lspci -nn | grep -i nvidia | head -1 | cut -c1-70)"
|
|
else
|
|
warn "GTX 1070" "not seen on PCI bus"
|
|
fi
|
|
if [ -e /dev/dri/renderD128 ]; then
|
|
note "/dev/dri/renderD128" "present"
|
|
note "render group gid" "$(stat -c '%g (%G)' /dev/dri/renderD128)"
|
|
else
|
|
warn "/dev/dri/renderD128" "missing — Quick Sync unavailable until iGPU is enabled in BIOS"
|
|
fi
|
|
|
|
echo "--- runtime ---"
|
|
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
|
|
note "docker" "$(docker --version | cut -d, -f1)"
|
|
else
|
|
bad "docker" "not running"
|
|
fi
|
|
note "media uid/gid" "$(id -u media 2>/dev/null || echo '?'):$(getent group media | cut -d: -f3 2>/dev/null || echo '?')"
|
|
note "swap" "$(free -h | awk '/Swap:/{print $2}')"
|
|
|
|
echo
|
|
[ $RC -eq 0 ] && echo " preflight clean" || echo " preflight found problems above"
|
|
# Preflight never blocks the rest of the bootstrap — it reports.
|
|
exit 0
|