#!/bin/bash # Debian 13 host installer for vanguard-stream. Run with sudo from this directory. set -euo pipefail cd "$(dirname "$0")" [[ $EUID -eq 0 ]] || { echo "run with sudo"; exit 1; } OWNER="${SUDO_USER:-debian}" # the login user; owns /srv/media and may run docker echo "== packages" apt-get update -q apt-get install -y -q ca-certificates curl gnupg ffmpeg jq psmisc rclone python3 python3-yaml xfsprogs git nodejs npm rsync if ! command -v docker >/dev/null; then echo "== docker" install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ > /etc/apt/sources.list.d/docker.list apt-get update -q apt-get install -y -q docker-ce docker-ce-cli containerd.io docker-compose-plugin fi echo "== timezone: server clock, logs, timers and the dump window all in Eastern" timedatectl set-timezone "${VG_TZ:-America/New_York}" timedatectl set-ntp true echo " timezone is: $(timedatectl show -p Timezone --value) (override: VG_TZ=... bash install.sh)" echo "== docker waits for /srv/media (never record onto the OS disk)" install -d /etc/systemd/system/docker.service.d cat > /etc/systemd/system/docker.service.d/10-srv-media.conf <<'DROP' [Unit] RequiresMountsFor=/srv/media DROP systemctl daemon-reload systemctl restart docker 2>/dev/null || true echo "== media user + folders" # media files are owned by uid 1000 — on OVH's template that is the 'debian' user getent passwd 1000 >/dev/null || useradd -r -u 1000 -d /srv/media -s /usr/sbin/nologin media usermod -aG docker "$OWNER" 2>/dev/null || true install -d -o 1000 -g 1000 /srv/media/{streams,clips,finals,trash,.markers,.syncthing} install -d -o 1000 -g 1000 /srv/appdata/jellyfin/{config,cache} /srv/appdata/authentik/{media,templates,certs} install -d /srv/appdata/authentik/{db,redis} /srv/appdata/mariadb install -d -o 1000 -g 1000 /srv/appdata/discord-bot /srv/appdata/gitea /srv/appdata/act_runner install -d -o "$OWNER" /srv/live # live portal static build (deployLive.sh) install -d -o 1000 -g 1000 /srv/media/streams/{techy,diceofire} /srv/media/clips/{techy,diceofire} for d in /srv/media/clips/*/; do [[ -e "$d/.stignore" ]] || install -m 0644 -o 1000 -g 1000 syncthing/stignore "$d/.stignore" done echo "== host scripts + units" install -m 0755 bin/normalize-sweep.sh bin/trash-purge.sh bin/dumpd.py bin/vgstrip.py /usr/local/bin/ install -d -m 0750 /etc/vanguard install -m 0640 dump-users.txt /etc/vanguard/dump-users.txt install -m 0644 systemd/*.service systemd/*.timer /etc/systemd/system/ systemctl daemon-reload systemctl enable --now normalize-sweep.timer trash-purge.timer dumpd.service echo "== sysctl" cat > /etc/sysctl.d/90-vanguard.conf <<'SYS' vm.swappiness = 10 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 SYS sysctl -q --system echo "== compose images (the stack is started by ./configure.py --full, which generates .env first)" chown -R "$OWNER:" . docker compose pull -q --ignore-buildable 2>/dev/null || docker compose pull -q || true echo "== host firewall (nftables; own table, input only; keeps this SSH session alive)" if [[ -f firewall/firewall.sh ]]; then bash firewall/firewall.sh else echo " firewall/firewall.sh missing — skipped" fi cat <<'MSG' == done. Next: ./configure.py --full (hostnames, passwords, .env secrets; starts the containers) == firewall is active: open a SECOND terminal and confirm ssh still works before closing this one. == log out and back in once so the docker group applies; then 'docker compose' works without sudo. == syncthing UI: ssh -L 8384:127.0.0.1:8384 debian@SERVER -> http://localhost:8384 MSG