Barazo Docker Compose templates for self-hosting
barazo.forum
1#!/usr/bin/env bash
2# Barazo Restore Script
3#
4# Restores a PostgreSQL backup from a file created by backup.sh.
5#
6# Usage:
7# ./scripts/restore.sh backups/barazo-backup-20260214-020000.sql.gz
8# ./scripts/restore.sh backups/barazo-backup-20260214-020000.sql.gz.age
9#
10# For encrypted backups (.age), set BACKUP_PRIVATE_KEY_FILE to the path
11# of your age private key file.
12#
13# WARNING: This will overwrite the current database contents.
14
15set -euo pipefail
16
17COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
18
19if [ $# -lt 1 ]; then
20 echo "Usage: $0 <backup-file>" >&2
21 echo "" >&2
22 echo "Examples:" >&2
23 echo " $0 backups/barazo-backup-20260214-020000.sql.gz" >&2
24 echo " $0 backups/barazo-backup-20260214-020000.sql.gz.age" >&2
25 echo "" >&2
26 echo "Environment variables:" >&2
27 echo " BACKUP_PRIVATE_KEY_FILE Path to age private key (for .age files)" >&2
28 echo " COMPOSE_FILE Docker Compose file (default: docker-compose.yml)" >&2
29 exit 1
30fi
31
32BACKUP_FILE="$1"
33
34if [ ! -f "$BACKUP_FILE" ]; then
35 echo "Error: Backup file not found: $BACKUP_FILE" >&2
36 exit 1
37fi
38
39# Check if encrypted
40IS_ENCRYPTED=false
41if [[ "$BACKUP_FILE" == *.age ]]; then
42 IS_ENCRYPTED=true
43 if [ -z "${BACKUP_PRIVATE_KEY_FILE:-}" ]; then
44 echo "Error: Encrypted backup requires BACKUP_PRIVATE_KEY_FILE environment variable" >&2
45 exit 1
46 fi
47 if [ ! -f "$BACKUP_PRIVATE_KEY_FILE" ]; then
48 echo "Error: Private key file not found: $BACKUP_PRIVATE_KEY_FILE" >&2
49 exit 1
50 fi
51 if ! command -v age &>/dev/null; then
52 echo "Error: age is required for decryption. Install: https://github.com/FiloSottile/age" >&2
53 exit 1
54 fi
55fi
56
57# Confirm
58echo "WARNING: This will overwrite the current database."
59echo "Backup file: $BACKUP_FILE"
60echo ""
61read -p "Continue? (y/N) " -r
62if [[ ! $REPLY =~ ^[Yy]$ ]]; then
63 echo "Restore cancelled."
64 exit 0
65fi
66
67# Check PostgreSQL is running
68if ! docker compose -f "$COMPOSE_FILE" exec -T postgres pg_isready -U "${POSTGRES_USER:-barazo}" &>/dev/null; then
69 echo "Error: PostgreSQL is not running. Start it with: docker compose -f $COMPOSE_FILE up -d postgres" >&2
70 exit 1
71fi
72
73echo "Starting restore at $(date)"
74
75# Stop API and Web to prevent writes during restore
76echo "Stopping API and Web services..."
77docker compose -f "$COMPOSE_FILE" stop barazo-api barazo-web 2>/dev/null || true
78
79# Restore
80DB_NAME="${POSTGRES_DB:-barazo}"
81DB_USER="${POSTGRES_USER:-barazo}"
82
83echo "Dropping and recreating database..."
84docker compose -f "$COMPOSE_FILE" exec -T postgres \
85 psql -U "$DB_USER" -d postgres -c "DROP DATABASE IF EXISTS $DB_NAME;"
86docker compose -f "$COMPOSE_FILE" exec -T postgres \
87 psql -U "$DB_USER" -d postgres -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;"
88
89echo "Restoring from backup..."
90if [ "$IS_ENCRYPTED" = true ]; then
91 age -d -i "$BACKUP_PRIVATE_KEY_FILE" "$BACKUP_FILE" \
92 | gunzip \
93 | docker compose -f "$COMPOSE_FILE" exec -T postgres psql -U "$DB_USER" -d "$DB_NAME" -q
94else
95 gunzip -c "$BACKUP_FILE" \
96 | docker compose -f "$COMPOSE_FILE" exec -T postgres psql -U "$DB_USER" -d "$DB_NAME" -q
97fi
98
99# Restart services
100echo "Restarting API and Web services..."
101docker compose -f "$COMPOSE_FILE" up -d barazo-api barazo-web
102
103# Verify
104echo "Verifying restore..."
105sleep 5
106TABLE_COUNT=$(docker compose -f "$COMPOSE_FILE" exec -T postgres \
107 psql -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';")
108echo "Tables in database: $(echo "$TABLE_COUNT" | tr -d ' ')"
109
110echo ""
111echo "Restore complete at $(date)"
112echo ""
113echo "IMPORTANT: If this backup is older than the latest data, check the"
114echo "deletion_log table and re-apply any deletions that occurred after"
115echo "the backup timestamp to maintain GDPR compliance."