#!/bin/bash # unraid-1 installer — sets up the unraid box with the standard fleet of # containers (garage S3, basement UI, watchtower, host-agent). # # Usage on the unraid console: # bash <(curl -sL https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1/install.sh) # # What it does: # 1. Validates this is an unraid host with the array started. # 2. Confirms the "s3" share exists (parity-protected; you create it). # 3. mkdir's /mnt/user/appdata/garage/{meta,} and /mnt/user/s3/data. # 4. Generates 3 garage secrets (rpc, admin, metrics) → secrets.env (preserved). # 5. Fetches garage.toml → /mnt/user/appdata/garage/garage.toml. # 6. Fetches 4 unraid container templates (garage / basement / watchtower / # host-agent), substitutes secrets where needed, writes them to # /boot/config/plugins/dockerMan/templates-user/my-*.xml. # # After: Unraid UI → Docker → Add Container → Template dropdown → Apply each. # # Idempotent: re-running keeps existing secrets and garage.toml. FORCE=1 # rotates the garage secrets (you'll then need to re-Apply garage + basement). set -euo pipefail BASE="${UNRAID1_INSTALL_BASE:-https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1}" APPDATA=/mnt/user/appdata/garage DATADIR=/mnt/user/s3/data TEMPLATE_DIR=/boot/config/plugins/dockerMan/templates-user TOML_DEST="$APPDATA/garage.toml" SECRETS="$APPDATA/secrets.env" say() { printf "install.sh: %s\n" "$*"; } die() { printf "install.sh: %s\n" "$*" >&2; exit 1; } # ── Sanity ── [ -d /boot/config/plugins/dockerMan ] || die "no /boot/config/plugins/dockerMan — not an unraid host?" [ -d /mnt/user ] || die "/mnt/user missing — is the array started?" [ -d /mnt/user/s3 ] || die "share /mnt/user/s3 missing. Create it in unraid UI first (Shares → Add Share → 's3', cache:yes recommended), then re-run." mkdir -p "$APPDATA/meta" "$DATADIR" "$TEMPLATE_DIR" /mnt/user/appdata/basement /mnt/user/appdata/host-agent # ── garage.toml ── if [ -f "$TOML_DEST" ] && [ "${FORCE:-}" != "1" ]; then say "$TOML_DEST exists — keeping (FORCE=1 to overwrite)" else say "fetching $BASE/garage.toml" curl -fsSL "$BASE/garage.toml" -o "$TOML_DEST" fi # ── Secrets ── if [ -f "$SECRETS" ] && [ "${FORCE:-}" != "1" ]; then say "reusing existing secrets at $SECRETS (FORCE=1 to rotate)" # shellcheck disable=SC1090 . "$SECRETS" else GARAGE_RPC_SECRET=$(openssl rand -hex 32) GARAGE_ADMIN_TOKEN=$(openssl rand -hex 32) GARAGE_METRICS_TOKEN=$(openssl rand -hex 32) BASEMENT_JWT_SECRET=$(openssl rand -hex 32) umask 077 cat > "$SECRETS" <> "$SECRETS" say "backfilled BASEMENT_JWT_SECRET into $SECRETS" fi # ── Templates ── # Fetch each, optionally sed-substitute __PLACEHOLDER__ secrets, write to # templates-user/. Secrets are hex chars only — safe in sed delimiters. fetch_template() { local src=$1 dest=$2 tmp shift 2 tmp=$(mktemp) say "fetching $src" curl -fsSL "$src" -o "$tmp" if [ "$#" -gt 0 ]; then sed "$@" "$tmp" > "$dest" else cp "$tmp" "$dest" fi rm -f "$tmp" say "wrote $dest" } fetch_template \ "$BASE/garage.template.xml" \ "$TEMPLATE_DIR/my-garage.xml" \ -e "s|__GARAGE_RPC_SECRET__|$GARAGE_RPC_SECRET|" \ -e "s|__GARAGE_ADMIN_TOKEN__|$GARAGE_ADMIN_TOKEN|" \ -e "s|__GARAGE_METRICS_TOKEN__|$GARAGE_METRICS_TOKEN|" fetch_template \ "$BASE/basement.template.xml" \ "$TEMPLATE_DIR/my-basement.xml" \ -e "s|__GARAGE_ADMIN_TOKEN__|$GARAGE_ADMIN_TOKEN|" \ -e "s|__BASEMENT_JWT_SECRET__|$BASEMENT_JWT_SECRET|" fetch_template \ "$BASE/watchtower.template.xml" \ "$TEMPLATE_DIR/my-watchtower.xml" fetch_template \ "$BASE/host-agent.template.xml" \ "$TEMPLATE_DIR/my-host-agent.xml" cat <<'EOF' ──────────────────────────────────────── Done. Apply each template in Unraid UI → Docker → Add Container → Template dropdown: 1. "my-garage" → Apply. Initialise cluster after first start: docker exec garage /garage status # copy node id docker exec garage /garage layout assign -z dc1 -c 1T docker exec garage /garage layout apply --version 1 Smoke test from your laptop: aws --endpoint-url https://s3.pq.io --region garage s3 ls 2. "my-basement" → Apply. Reachable at https://basement.pq.io. 3. "my-watchtower" → Apply. Auto-updates the labelled containers above + below. 4. "my-host-agent" → Apply. Pushes node/cadvisor/smartctl metrics to your Prometheus (defaults to docker-1.internal.pq.io:9090). ──────────────────────────────────────── EOF