Add pg_dump/pg_restore backup and restore scripts
For moving this project to a new server: git tracks code, not the Postgres data directory, and raw volume/data-dir copying is fragile across Postgres versions or architectures (this repo already hit a collation-version mismatch once from just changing the Postgres image). pg_dump/pg_restore is the portable way to move the actual data instead. scripts/backup-db.sh dumps the running postgres container's database in pg_dump's custom format into ./backups/ (gitignored — dumps contain real user data and don't belong in git history). It reads POSTGRES_USER/POSTGRES_DB from the container's own environment rather than re-parsing .env, so it can't drift out of sync with the actual running config. scripts/restore-db.sh restores a dump into the running container (--clean --if-exists --single-transaction, so a failure rolls back atomically instead of leaving a half-restored database), with a confirmation prompt since it's destructive, skippable via -y. Added .gitattributes forcing LF on *.sh — Windows git would otherwise check these out as CRLF, breaking the bash shebang on the Linux server they're meant to run on. Verified end to end: took a real backup of the live database, restored it back over itself, and confirmed exact row-count matches across every table plus that pgvector similarity search still works on the restored embeddings (not just that the bytes round-tripped). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
* text=auto
|
||||
*.sh text eol=lf
|
||||
@@ -12,6 +12,7 @@ build/
|
||||
caddy_data/
|
||||
caddy_config/
|
||||
pgdata/
|
||||
backups/
|
||||
frontend/dist/
|
||||
*.tsbuildinfo
|
||||
.DS_Store
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# Dumps the Postgres database from the running "postgres" container into ./backups/,
|
||||
# in pg_dump's custom format (compressed, restorable with pg_restore — see restore-db.sh).
|
||||
#
|
||||
# Usage: scripts/backup-db.sh
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
mkdir -p backups
|
||||
timestamp="$(date +%Y%m%d_%H%M%S)"
|
||||
out="backups/dnd_backup_${timestamp}.dump"
|
||||
trap 'rm -f "${out}"' ERR
|
||||
|
||||
echo "Dumping database into ${out} ..."
|
||||
docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -F c' > "${out}"
|
||||
|
||||
size="$(du -h "${out}" | cut -f1)"
|
||||
echo "Done: ${out} (${size})"
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# Restores a dump produced by backup-db.sh into the running "postgres" container.
|
||||
# Destructive: drops and recreates every object in the target database before restoring.
|
||||
#
|
||||
# Usage: scripts/restore-db.sh path/to/dnd_backup_20260101_120000.dump [-y]
|
||||
# -y skip the confirmation prompt (for non-interactive/scripted use)
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
skip_confirm=false
|
||||
file=""
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "-y" ]; then
|
||||
skip_confirm=true
|
||||
else
|
||||
file="$arg"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$file" ]; then
|
||||
echo "Usage: $0 path/to/backup.dump [-y]" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "Error: file not found: $file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$skip_confirm" = false ]; then
|
||||
read -r -p "This will DROP and recreate every table in the target database. Continue? [y/N] " reply
|
||||
case "$reply" in
|
||||
[yY]|[yY][eE][sS]) ;;
|
||||
*) echo "Aborted."; exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "Restoring ${file} ..."
|
||||
docker compose exec -T postgres sh -c \
|
||||
'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists --no-owner --no-privileges --single-transaction' \
|
||||
< "$file"
|
||||
|
||||
echo "Done."
|
||||
Reference in New Issue
Block a user