#!/usr/bin/env bash # Nightly logical backups for both MosswartOverlord databases. # Install as a cron job on the live host (see docs/backups.md). Note `bash` # in the cron line (survives a lost executable bit) and that /home/erik/backups # must exist BEFORE the first run (cron sets up the >> redirection before this # script's mkdir runs): # 15 3 * * * bash /home/erik/MosswartOverlord/scripts/backup-databases.sh >> /home/erik/backups/backup.log 2>&1 # # What is backed up: # - dereth (TimescaleDB): full schema + all data EXCEPT the raw # telemetry_events/spawn_events hypertable chunks. Those tables hold # ~12 GB of data that expires via retention policies in 7-30 days # anyway; the irreplaceable rows (users, char_stats, rare_stats, # rare_events, combat_stats*, portals, character_stats, server_status) # are all included. # - inventory_db (postgres): full dump (~1 GB raw, much smaller compressed). # # Restore procedure: docs/backups.md (TimescaleDB needs pre/post restore calls). set -euo pipefail # Dumps contain the users table (bcrypt hashes) — keep them owner-only. umask 077 BACKUP_DIR="${BACKUP_DIR:-/home/erik/backups/postgres}" KEEP_DAYS="${KEEP_DAYS:-7}" STAMP="$(date -u +%Y%m%d-%H%M)" mkdir -p "$BACKUP_DIR" # dereth: -Fc is compressed; exclude hypertable chunk DATA (schema kept so a # restore recreates the tables empty and retention/compression jobs reattach). docker exec dereth-db pg_dump -U postgres -Fc \ --exclude-table-data='public.telemetry_events' \ --exclude-table-data='public.spawn_events' \ --exclude-table-data='_timescaledb_internal._hyper_*' \ dereth > "$BACKUP_DIR/dereth-$STAMP.dump.tmp" # Canary: a healthy dereth dump is ~50 MB; a tiny one means pg_dump silently # produced garbage (fail the run so the old dumps are kept and cron logs it). if [ "$(stat -c%s "$BACKUP_DIR/dereth-$STAMP.dump.tmp")" -lt 10000000 ]; then echo "$(date -u +%FT%TZ) FAIL dereth dump under 10MB — keeping old backups" >&2 exit 1 fi mv "$BACKUP_DIR/dereth-$STAMP.dump.tmp" "$BACKUP_DIR/dereth-$STAMP.dump" docker exec inventory-db pg_dump -U inventory_user -Fc inventory_db \ > "$BACKUP_DIR/inventory-$STAMP.dump.tmp" mv "$BACKUP_DIR/inventory-$STAMP.dump.tmp" "$BACKUP_DIR/inventory-$STAMP.dump" # Retention: keep KEEP_DAYS days of dailies. find "$BACKUP_DIR" -name 'dereth-*.dump' -mtime +"$KEEP_DAYS" -delete find "$BACKUP_DIR" -name 'inventory-*.dump' -mtime +"$KEEP_DAYS" -delete # Clean up aborted runs older than a day. find "$BACKUP_DIR" -name '*.dump.tmp' -mtime +1 -delete echo "$(date -u +%FT%TZ) OK dereth=$(du -h "$BACKUP_DIR/dereth-$STAMP.dump" | cut -f1) inventory=$(du -h "$BACKUP_DIR/inventory-$STAMP.dump" | cut -f1)"