#!/usr/bin/env bash # scripts/restic-init.sh # # One-time bootstrap for the Phase 2 restic backup chain. Run this on # nullstone as root (sudo) AFTER `apt install restic mcrcon`. # # What it does: # 1. Generates /etc/mc-backup.pw (40-byte random restic password) if absent. # 2. Writes /etc/mc-backup.env (consumed by restic-backup-playerdata.sh). # 3. Initialises the local restic repo at /home/user/restic/mc-frequent. # 4. Takes a baseline snapshot so the timer's first run is fast. # 5. Optionally adds an SFTP-mirror block once onyx is provisioned. # # Idempotent — re-running is safe; existing files are preserved. # # Cross-ref: docs/BACKUP-STRATEGY.md §8.2, docs/RUNBOOK-BACKUP-RESTORE.md. set -euo pipefail umask 077 if [ "$(id -u)" -ne 0 ]; then echo "FATAL: must run as root (sudo)." >&2 exit 2 fi if ! command -v restic >/dev/null 2>&1; then echo "FATAL: restic not installed. Run: apt install restic mcrcon" >&2 exit 3 fi # Resolve target user — restic repo lives under their home so /opt # disk pressure doesn't matter. nullstone: 142G free on /home. TARGET_USER="${TARGET_USER:-user}" if ! id "$TARGET_USER" >/dev/null 2>&1; then echo "FATAL: user '$TARGET_USER' not found" >&2 exit 4 fi TARGET_HOME=$(getent passwd "$TARGET_USER" | cut -d: -f6) PW_FILE="/etc/mc-backup.pw" ENV_FILE="/etc/mc-backup.env" REPO_FREQUENT="${TARGET_HOME}/restic/mc-frequent" REPO_WORLD="${TARGET_HOME}/restic/mc-world" LOG_DIR="/var/log" SENTINEL_DIR="/var/lib/mc-backup" # 1. Password file if [ ! -e "$PW_FILE" ]; then head -c 40 /dev/urandom | base64 > "$PW_FILE" chown root:root "$PW_FILE" chmod 600 "$PW_FILE" echo "Generated $PW_FILE (40 bytes random)." else echo "$PW_FILE already exists — keeping." fi # 2. Env file (only created if missing; user can edit afterwards). if [ ! -e "$ENV_FILE" ]; then cat > "$ENV_FILE" </dev/null \ || chown "$TARGET_USER":"$(id -gn "$TARGET_USER")" "$LOG_DIR/mc-backup.log" chmod 640 "$LOG_DIR/mc-backup.log" # 4. Repo init (idempotent — restic init exits non-zero if repo exists). init_repo() { local repo=$1 install -d -o "$TARGET_USER" -g "$(id -gn "$TARGET_USER")" -m 700 \ "$(dirname "$repo")" "$repo" if RESTIC_PASSWORD_FILE="$PW_FILE" RESTIC_REPOSITORY="$repo" \ runuser -u "$TARGET_USER" -- restic snapshots >/dev/null 2>&1; then echo "Repo $repo: already initialised." else RESTIC_PASSWORD_FILE="$PW_FILE" RESTIC_REPOSITORY="$repo" \ runuser -u "$TARGET_USER" -- restic init echo "Repo $repo: initialised." fi } init_repo "$REPO_FREQUENT" init_repo "$REPO_WORLD" # 5. Baseline snapshot of the frequent repo so the first timer run is fast. echo "Taking baseline snapshot into $REPO_FREQUENT ..." runuser -u "$TARGET_USER" -- env \ RESTIC_PASSWORD_FILE="$PW_FILE" \ RESTIC_REPOSITORY="$REPO_FREQUENT" \ restic backup \ --tag playerdata --tag baseline --host "$(hostname)" \ --exclude='*.lock' --exclude='*.tmp' \ /opt/docker/minecraft/world/playerdata \ /opt/docker/minecraft/world/stats \ /opt/docker/minecraft/world/advancements \ /opt/docker/minecraft/homestead_data.db \ /opt/docker/minecraft/plugins/AuthMe \ /opt/docker/minecraft/plugins/CoreProtect/database.db \ /opt/docker/minecraft/plugins/LuckPerms \ || echo "Baseline snapshot returned non-zero — review output above." cat <<'NEXT' --------------------------------------------------------------- restic-init.sh complete. Next steps: 1. Install systemd units: install -m644 scripts/systemd/mc-backup-playerdata.service \ /etc/systemd/system/ install -m644 scripts/systemd/mc-backup-playerdata.timer \ /etc/systemd/system/ install -m755 scripts/restic-backup-playerdata.sh \ /usr/local/bin/ 2. systemctl daemon-reload 3. systemctl enable --now mc-backup-playerdata.timer 4. Tail: journalctl -u mc-backup-playerdata.service -f Onyx (off-host mirror) provisioning is a separate step — see docs/RUNBOOK-BACKUP-RESTORE.md "Phase 2 deployment". --------------------------------------------------------------- NEXT