docs: add comprehensive database migration and backup documentation
All checks were successful
Basil CI/CD Pipeline / Shared Package Tests (push) Successful in 1m38s
Basil CI/CD Pipeline / Security Scanning (push) Successful in 1m55s
Basil CI/CD Pipeline / Web Tests (push) Successful in 2m9s
Basil CI/CD Pipeline / Build All Packages (push) Successful in 1m31s
Basil CI/CD Pipeline / Code Linting (push) Successful in 1m57s
Basil CI/CD Pipeline / API Tests (push) Successful in 2m34s
Basil CI/CD Pipeline / E2E Tests (push) Has been skipped
Basil CI/CD Pipeline / Build & Push Docker Images (push) Successful in 5m5s
Basil CI/CD Pipeline / Trigger Deployment (push) Successful in 12s
All checks were successful
Basil CI/CD Pipeline / Shared Package Tests (push) Successful in 1m38s
Basil CI/CD Pipeline / Security Scanning (push) Successful in 1m55s
Basil CI/CD Pipeline / Web Tests (push) Successful in 2m9s
Basil CI/CD Pipeline / Build All Packages (push) Successful in 1m31s
Basil CI/CD Pipeline / Code Linting (push) Successful in 1m57s
Basil CI/CD Pipeline / API Tests (push) Successful in 2m34s
Basil CI/CD Pipeline / E2E Tests (push) Has been skipped
Basil CI/CD Pipeline / Build & Push Docker Images (push) Successful in 5m5s
Basil CI/CD Pipeline / Trigger Deployment (push) Successful in 12s
Added complete guide for migrating from containerized PostgreSQL to standalone server with production-grade backup strategies. New files: - docs/DATABASE-MIGRATION-GUIDE.md - Complete migration guide with step-by-step instructions, troubleshooting, and rollback procedures - scripts/backup-standalone-postgres.sh - Automated backup script with daily, weekly, and monthly retention policies - scripts/restore-standalone-postgres.sh - Safe restore script with verification and pre-restore safety backup Features: - Hybrid backup strategy (PostgreSQL native + Basil API) - Automated retention policy (30/90/365 days) - Integrity verification - Safety backups before restore - Complete troubleshooting guide - Rollback procedures Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
465
docs/DATABASE-MIGRATION-GUIDE.md
Normal file
465
docs/DATABASE-MIGRATION-GUIDE.md
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
# Database Migration Guide: Container → Standalone PostgreSQL
|
||||||
|
|
||||||
|
This guide covers migrating Basil from containerized PostgreSQL to a standalone PostgreSQL server and setting up production-grade backups.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
1. [Why Migrate?](#why-migrate)
|
||||||
|
2. [Pre-Migration Checklist](#pre-migration-checklist)
|
||||||
|
3. [Migration Steps](#migration-steps)
|
||||||
|
4. [Backup Strategy](#backup-strategy)
|
||||||
|
5. [Testing & Verification](#testing--verification)
|
||||||
|
6. [Rollback Plan](#rollback-plan)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why Migrate?
|
||||||
|
|
||||||
|
### Standalone PostgreSQL Advantages
|
||||||
|
- ✅ Dedicated database resources (no competition with app containers)
|
||||||
|
- ✅ Standard PostgreSQL backup/restore tools
|
||||||
|
- ✅ Point-in-time recovery (PITR) capabilities
|
||||||
|
- ✅ Better monitoring and administration
|
||||||
|
- ✅ Industry best practice for production
|
||||||
|
- ✅ Easier to scale independently
|
||||||
|
|
||||||
|
### When to Keep Containerized
|
||||||
|
- Local development environments
|
||||||
|
- Staging/test environments
|
||||||
|
- Simple single-server deployments
|
||||||
|
- Environments where simplicity > resilience
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-Migration Checklist
|
||||||
|
|
||||||
|
- [ ] Standalone PostgreSQL server is installed and accessible
|
||||||
|
- [ ] PostgreSQL version is 13 or higher (check: `psql --version`)
|
||||||
|
- [ ] Network connectivity from app server to DB server
|
||||||
|
- [ ] Firewall rules allow PostgreSQL port (default: 5432)
|
||||||
|
- [ ] You have PostgreSQL superuser credentials
|
||||||
|
- [ ] Current Basil data is backed up
|
||||||
|
- [ ] Maintenance window scheduled (expect ~15-30 min downtime)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Steps
|
||||||
|
|
||||||
|
### Step 1: Create Backup of Current Data
|
||||||
|
|
||||||
|
**Option A: Use Basil's Built-in API (Recommended)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create full backup (database + uploaded images)
|
||||||
|
curl -X POST http://localhost:3001/api/backup
|
||||||
|
|
||||||
|
# List available backups
|
||||||
|
curl http://localhost:3001/api/backup
|
||||||
|
|
||||||
|
# Download the latest backup
|
||||||
|
curl -O http://localhost:3001/api/backup/basil-backup-YYYY-MM-DDTHH-MM-SS.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B: Direct PostgreSQL Dump**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From container
|
||||||
|
docker exec basil-postgres pg_dump -U basil basil > /tmp/basil_migration.sql
|
||||||
|
|
||||||
|
# Verify backup
|
||||||
|
head -20 /tmp/basil_migration.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Prepare Standalone PostgreSQL Server
|
||||||
|
|
||||||
|
SSH into your PostgreSQL server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh your-postgres-server
|
||||||
|
|
||||||
|
# Switch to postgres user
|
||||||
|
sudo -u postgres psql
|
||||||
|
```
|
||||||
|
|
||||||
|
Create database and user:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Create database
|
||||||
|
CREATE DATABASE basil;
|
||||||
|
|
||||||
|
-- Create user with password
|
||||||
|
CREATE USER basil WITH ENCRYPTED PASSWORD 'your-secure-password-here';
|
||||||
|
|
||||||
|
-- Grant privileges
|
||||||
|
GRANT ALL PRIVILEGES ON DATABASE basil TO basil;
|
||||||
|
|
||||||
|
-- Connect to basil database
|
||||||
|
\c basil
|
||||||
|
|
||||||
|
-- Grant schema permissions
|
||||||
|
GRANT ALL ON SCHEMA public TO basil;
|
||||||
|
|
||||||
|
-- Exit
|
||||||
|
\q
|
||||||
|
```
|
||||||
|
|
||||||
|
**Security Best Practices:**
|
||||||
|
```bash
|
||||||
|
# Generate strong password
|
||||||
|
openssl rand -base64 32
|
||||||
|
|
||||||
|
# Store in password manager or .pgpass file
|
||||||
|
echo "your-postgres-server:5432:basil:basil:your-password" >> ~/.pgpass
|
||||||
|
chmod 600 ~/.pgpass
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Update Firewall Rules
|
||||||
|
|
||||||
|
On PostgreSQL server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Allow app server to connect
|
||||||
|
sudo ufw allow from <app-server-ip> to any port 5432
|
||||||
|
|
||||||
|
# Or edit pg_hba.conf
|
||||||
|
sudo nano /etc/postgresql/15/main/pg_hba.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Add line:
|
||||||
|
```
|
||||||
|
host basil basil <app-server-ip>/32 scram-sha-256
|
||||||
|
```
|
||||||
|
|
||||||
|
Reload PostgreSQL:
|
||||||
|
```bash
|
||||||
|
sudo systemctl reload postgresql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Test Connectivity
|
||||||
|
|
||||||
|
From app server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test connection
|
||||||
|
psql -h your-postgres-server -U basil -d basil -c "SELECT version();"
|
||||||
|
|
||||||
|
# Should show PostgreSQL version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Update Basil Configuration
|
||||||
|
|
||||||
|
**On app server**, update environment configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit .env file
|
||||||
|
cd /srv/docker-compose/basil
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Add or update:
|
||||||
|
```bash
|
||||||
|
DATABASE_URL=postgresql://basil:your-password@your-postgres-server-ip:5432/basil?schema=public
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update docker-compose.yml:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
# ... other variables
|
||||||
|
|
||||||
|
# Comment out postgres service
|
||||||
|
# postgres:
|
||||||
|
# image: postgres:15
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Run Prisma Migrations
|
||||||
|
|
||||||
|
This creates the schema on your new database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/pkartch/development/basil/packages/api
|
||||||
|
|
||||||
|
# Generate Prisma client
|
||||||
|
npm run prisma:generate
|
||||||
|
|
||||||
|
# Deploy migrations
|
||||||
|
npm run prisma:migrate deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 7: Restore Data
|
||||||
|
|
||||||
|
**Option A: Use Basil's Restore API**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy backup to server (if needed)
|
||||||
|
scp basil-backup-*.zip app-server:/tmp/
|
||||||
|
|
||||||
|
# Restore via API
|
||||||
|
curl -X POST http://localhost:3001/api/backup/restore \
|
||||||
|
-F "backup=@/tmp/basil-backup-YYYY-MM-DDTHH-MM-SS.zip"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B: Direct PostgreSQL Restore**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy SQL dump to DB server
|
||||||
|
scp /tmp/basil_migration.sql your-postgres-server:/tmp/
|
||||||
|
|
||||||
|
# On PostgreSQL server
|
||||||
|
psql -h localhost -U basil basil < /tmp/basil_migration.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 8: Restart Application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/docker-compose/basil
|
||||||
|
./dev-rebuild.sh
|
||||||
|
|
||||||
|
# Or
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 9: Verify Migration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check API logs
|
||||||
|
docker-compose logs api | grep -i "database\|connected"
|
||||||
|
|
||||||
|
# Test API
|
||||||
|
curl http://localhost:3001/api/recipes
|
||||||
|
curl http://localhost:3001/api/cookbooks
|
||||||
|
|
||||||
|
# Check database directly
|
||||||
|
psql -h your-postgres-server -U basil basil -c "SELECT COUNT(*) FROM \"Recipe\";"
|
||||||
|
psql -h your-postgres-server -U basil basil -c "SELECT COUNT(*) FROM \"Cookbook\";"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup Strategy
|
||||||
|
|
||||||
|
### Daily Automated Backups
|
||||||
|
|
||||||
|
**On PostgreSQL server:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy backup script to server
|
||||||
|
scp scripts/backup-standalone-postgres.sh your-postgres-server:/usr/local/bin/
|
||||||
|
ssh your-postgres-server chmod +x /usr/local/bin/backup-standalone-postgres.sh
|
||||||
|
|
||||||
|
# Set up cron job
|
||||||
|
ssh your-postgres-server
|
||||||
|
sudo crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Add:
|
||||||
|
```cron
|
||||||
|
# Daily backup at 2 AM
|
||||||
|
0 2 * * * /usr/local/bin/backup-standalone-postgres.sh >> /var/log/basil-backup.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Weekly Application Backups
|
||||||
|
|
||||||
|
**On app server:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Add:
|
||||||
|
```cron
|
||||||
|
# Weekly full backup (DB + images) on Sundays at 3 AM
|
||||||
|
0 3 * * 0 curl -X POST http://localhost:3001/api/backup >> /var/log/basil-api-backup.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Off-Site Backup Sync
|
||||||
|
|
||||||
|
**Set up rsync to NAS or remote server:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On PostgreSQL server
|
||||||
|
sudo crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Add:
|
||||||
|
```cron
|
||||||
|
# Sync backups to NAS at 4 AM
|
||||||
|
0 4 * * * rsync -av /var/backups/basil/ /mnt/nas/backups/basil/ >> /var/log/basil-sync.log 2>&1
|
||||||
|
|
||||||
|
# Optional: Upload to S3
|
||||||
|
0 5 * * * aws s3 sync /var/backups/basil/ s3://your-bucket/basil-backups/ --storage-class GLACIER >> /var/log/basil-s3.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Retention
|
||||||
|
|
||||||
|
The backup script automatically maintains:
|
||||||
|
- **Daily backups:** 30 days
|
||||||
|
- **Weekly backups:** 90 days (12 weeks)
|
||||||
|
- **Monthly backups:** 365 days (12 months)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing & Verification
|
||||||
|
|
||||||
|
### Test Backup Process
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run backup manually
|
||||||
|
/usr/local/bin/backup-standalone-postgres.sh
|
||||||
|
|
||||||
|
# Verify backup exists
|
||||||
|
ls -lh /var/backups/basil/daily/
|
||||||
|
|
||||||
|
# Test backup integrity
|
||||||
|
gzip -t /var/backups/basil/daily/basil-*.sql.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Restore Process
|
||||||
|
|
||||||
|
**On a test server (NOT production!):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy restore script
|
||||||
|
scp scripts/restore-standalone-postgres.sh test-server:/tmp/
|
||||||
|
|
||||||
|
# Run restore
|
||||||
|
/tmp/restore-standalone-postgres.sh /var/backups/basil/daily/basil-YYYYMMDD.sql.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
|
||||||
|
**Set up monitoring checks:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check backup file age (should be < 24 hours)
|
||||||
|
find /var/backups/basil/daily/ -name "basil-*.sql.gz" -mtime -1 | grep -q . || echo "ALERT: No recent backup!"
|
||||||
|
|
||||||
|
# Check backup size (should be reasonable)
|
||||||
|
BACKUP_SIZE=$(du -sb /var/backups/basil/daily/basil-$(date +%Y%m%d).sql.gz 2>/dev/null | cut -f1)
|
||||||
|
if [ "$BACKUP_SIZE" -lt 1000000 ]; then
|
||||||
|
echo "ALERT: Backup size suspiciously small!"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If migration fails, you can quickly rollback:
|
||||||
|
|
||||||
|
### Quick Rollback to Containerized PostgreSQL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/docker-compose/basil
|
||||||
|
|
||||||
|
# 1. Restore old docker-compose.yml (uncomment postgres service)
|
||||||
|
nano docker-compose.yml
|
||||||
|
|
||||||
|
# 2. Remove DATABASE_URL override
|
||||||
|
nano .env # Comment out or remove DATABASE_URL
|
||||||
|
|
||||||
|
# 3. Restart with containerized database
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 4. Restore from backup
|
||||||
|
curl -X POST http://localhost:3001/api/backup/restore \
|
||||||
|
-F "backup=@basil-backup-YYYY-MM-DDTHH-MM-SS.zip"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Recovery
|
||||||
|
|
||||||
|
If you need to recover data from standalone server after rollback:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dump from standalone server
|
||||||
|
ssh your-postgres-server
|
||||||
|
pg_dump -U basil basil > /tmp/basil_recovery.sql
|
||||||
|
|
||||||
|
# Import to containerized database
|
||||||
|
docker exec -i basil-postgres psql -U basil basil < /tmp/basil_recovery.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Connection Issues
|
||||||
|
|
||||||
|
**Error: "Connection refused"**
|
||||||
|
```bash
|
||||||
|
# Check PostgreSQL is listening on network
|
||||||
|
sudo netstat -tlnp | grep 5432
|
||||||
|
|
||||||
|
# Verify postgresql.conf
|
||||||
|
grep "listen_addresses" /etc/postgresql/*/main/postgresql.conf
|
||||||
|
# Should be: listen_addresses = '*'
|
||||||
|
|
||||||
|
# Restart PostgreSQL
|
||||||
|
sudo systemctl restart postgresql
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error: "Authentication failed"**
|
||||||
|
```bash
|
||||||
|
# Verify user exists
|
||||||
|
psql -U postgres -c "\du basil"
|
||||||
|
|
||||||
|
# Reset password
|
||||||
|
psql -U postgres -c "ALTER USER basil WITH PASSWORD 'new-password';"
|
||||||
|
|
||||||
|
# Check pg_hba.conf authentication method
|
||||||
|
sudo cat /etc/postgresql/*/main/pg_hba.conf | grep basil
|
||||||
|
```
|
||||||
|
|
||||||
|
### Migration Issues
|
||||||
|
|
||||||
|
**Error: "Relation already exists"**
|
||||||
|
```bash
|
||||||
|
# Drop and recreate database
|
||||||
|
psql -U postgres -c "DROP DATABASE basil;"
|
||||||
|
psql -U postgres -c "CREATE DATABASE basil;"
|
||||||
|
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE basil TO basil;"
|
||||||
|
|
||||||
|
# Re-run migrations
|
||||||
|
cd packages/api
|
||||||
|
npm run prisma:migrate deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error: "Foreign key constraint violation"**
|
||||||
|
```bash
|
||||||
|
# Restore with --no-owner --no-privileges flags
|
||||||
|
pg_restore --no-owner --no-privileges -U basil -d basil backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
- [PostgreSQL Backup Documentation](https://www.postgresql.org/docs/current/backup.html)
|
||||||
|
- [Prisma Migration Guide](https://www.prisma.io/docs/concepts/components/prisma-migrate)
|
||||||
|
- [Docker PostgreSQL Volume Management](https://docs.docker.com/storage/volumes/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary Checklist
|
||||||
|
|
||||||
|
Post-migration verification:
|
||||||
|
|
||||||
|
- [ ] Application connects to standalone PostgreSQL
|
||||||
|
- [ ] All recipes visible in UI
|
||||||
|
- [ ] All cookbooks visible in UI
|
||||||
|
- [ ] Recipe import works
|
||||||
|
- [ ] Image uploads work
|
||||||
|
- [ ] Daily backups running
|
||||||
|
- [ ] Weekly API backups running
|
||||||
|
- [ ] Backup integrity verified
|
||||||
|
- [ ] Restore process tested (on test server)
|
||||||
|
- [ ] Monitoring alerts configured
|
||||||
|
- [ ] Old containerized database backed up (for safety)
|
||||||
|
- [ ] Documentation updated with new DATABASE_URL
|
||||||
|
|
||||||
|
**Congratulations! You've successfully migrated to standalone PostgreSQL! 🎉**
|
||||||
74
scripts/backup-standalone-postgres.sh
Executable file
74
scripts/backup-standalone-postgres.sh
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Basil Backup Script for Standalone PostgreSQL
|
||||||
|
# Place on database server and run via cron
|
||||||
|
#
|
||||||
|
# Cron example (daily at 2 AM):
|
||||||
|
# 0 2 * * * /path/to/backup-standalone-postgres.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
DB_HOST="localhost"
|
||||||
|
DB_PORT="5432"
|
||||||
|
DB_NAME="basil"
|
||||||
|
DB_USER="basil"
|
||||||
|
BACKUP_DIR="/var/backups/basil"
|
||||||
|
RETENTION_DAYS=30
|
||||||
|
|
||||||
|
# Create backup directories
|
||||||
|
mkdir -p "$BACKUP_DIR/daily"
|
||||||
|
mkdir -p "$BACKUP_DIR/weekly"
|
||||||
|
mkdir -p "$BACKUP_DIR/monthly"
|
||||||
|
|
||||||
|
# Timestamp
|
||||||
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
|
DATE=$(date +%Y%m%d)
|
||||||
|
DAY_OF_WEEK=$(date +%u) # 1=Monday, 7=Sunday
|
||||||
|
DAY_OF_MONTH=$(date +%d)
|
||||||
|
|
||||||
|
# Daily backup
|
||||||
|
echo "Starting daily backup: $TIMESTAMP"
|
||||||
|
DAILY_BACKUP="$BACKUP_DIR/daily/basil-$DATE.sql.gz"
|
||||||
|
pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" "$DB_NAME" | gzip > "$DAILY_BACKUP"
|
||||||
|
echo "Daily backup completed: $DAILY_BACKUP"
|
||||||
|
|
||||||
|
# Weekly backup (on Sundays)
|
||||||
|
if [ "$DAY_OF_WEEK" -eq 7 ]; then
|
||||||
|
echo "Creating weekly backup"
|
||||||
|
WEEK=$(date +%V)
|
||||||
|
WEEKLY_BACKUP="$BACKUP_DIR/weekly/basil-week$WEEK-$DATE.sql.gz"
|
||||||
|
cp "$DAILY_BACKUP" "$WEEKLY_BACKUP"
|
||||||
|
echo "Weekly backup completed: $WEEKLY_BACKUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Monthly backup (on 1st of month)
|
||||||
|
if [ "$DAY_OF_MONTH" -eq 01 ]; then
|
||||||
|
echo "Creating monthly backup"
|
||||||
|
MONTH=$(date +%Y%m)
|
||||||
|
MONTHLY_BACKUP="$BACKUP_DIR/monthly/basil-$MONTH.sql.gz"
|
||||||
|
cp "$DAILY_BACKUP" "$MONTHLY_BACKUP"
|
||||||
|
echo "Monthly backup completed: $MONTHLY_BACKUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup old backups
|
||||||
|
echo "Cleaning up old backups..."
|
||||||
|
find "$BACKUP_DIR/daily" -name "basil-*.sql.gz" -mtime +$RETENTION_DAYS -delete
|
||||||
|
find "$BACKUP_DIR/weekly" -name "basil-*.sql.gz" -mtime +90 -delete
|
||||||
|
find "$BACKUP_DIR/monthly" -name "basil-*.sql.gz" -mtime +365 -delete
|
||||||
|
|
||||||
|
# Verify backup integrity
|
||||||
|
echo "Verifying backup integrity..."
|
||||||
|
if gzip -t "$DAILY_BACKUP"; then
|
||||||
|
BACKUP_SIZE=$(du -h "$DAILY_BACKUP" | cut -f1)
|
||||||
|
echo "Backup verification successful. Size: $BACKUP_SIZE"
|
||||||
|
else
|
||||||
|
echo "ERROR: Backup verification failed!" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Optional: Send notification (uncomment to enable)
|
||||||
|
# echo "Basil backup completed successfully on $(hostname) at $(date)" | \
|
||||||
|
# mail -s "Basil Backup Success" your-email@example.com
|
||||||
|
|
||||||
|
echo "Backup process completed successfully"
|
||||||
88
scripts/restore-standalone-postgres.sh
Executable file
88
scripts/restore-standalone-postgres.sh
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Basil Restore Script for Standalone PostgreSQL
|
||||||
|
# Run manually when you need to restore from backup
|
||||||
|
#
|
||||||
|
# Usage: ./restore-standalone-postgres.sh /path/to/backup.sql.gz
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
DB_HOST="localhost"
|
||||||
|
DB_PORT="5432"
|
||||||
|
DB_NAME="basil"
|
||||||
|
DB_USER="basil"
|
||||||
|
|
||||||
|
# Check arguments
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Usage: $0 /path/to/backup.sql.gz"
|
||||||
|
echo ""
|
||||||
|
echo "Available backups:"
|
||||||
|
echo "Daily:"
|
||||||
|
ls -lh /var/backups/basil/daily/ 2>/dev/null | tail -5
|
||||||
|
echo ""
|
||||||
|
echo "Weekly:"
|
||||||
|
ls -lh /var/backups/basil/weekly/ 2>/dev/null | tail -5
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BACKUP_FILE="$1"
|
||||||
|
|
||||||
|
# Verify backup file exists
|
||||||
|
if [ ! -f "$BACKUP_FILE" ]; then
|
||||||
|
echo "ERROR: Backup file not found: $BACKUP_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify backup integrity
|
||||||
|
echo "Verifying backup integrity..."
|
||||||
|
if ! gzip -t "$BACKUP_FILE"; then
|
||||||
|
echo "ERROR: Backup file is corrupted!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Confirm restore
|
||||||
|
echo "===== WARNING ====="
|
||||||
|
echo "This will DESTROY all current data in database: $DB_NAME"
|
||||||
|
echo "Backup file: $BACKUP_FILE"
|
||||||
|
echo "Database: $DB_USER@$DB_HOST:$DB_PORT/$DB_NAME"
|
||||||
|
echo ""
|
||||||
|
read -p "Are you sure you want to continue? (type 'yes' to confirm): " CONFIRM
|
||||||
|
|
||||||
|
if [ "$CONFIRM" != "yes" ]; then
|
||||||
|
echo "Restore cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create backup of current database before restore
|
||||||
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
|
PRE_RESTORE_BACKUP="/tmp/basil-pre-restore-$TIMESTAMP.sql.gz"
|
||||||
|
echo "Creating safety backup of current database..."
|
||||||
|
pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" "$DB_NAME" | gzip > "$PRE_RESTORE_BACKUP"
|
||||||
|
echo "Safety backup created: $PRE_RESTORE_BACKUP"
|
||||||
|
|
||||||
|
# Drop and recreate database
|
||||||
|
echo "Dropping existing database..."
|
||||||
|
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" postgres <<EOF
|
||||||
|
DROP DATABASE IF EXISTS $DB_NAME;
|
||||||
|
CREATE DATABASE $DB_NAME;
|
||||||
|
GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Restore from backup
|
||||||
|
echo "Restoring from backup..."
|
||||||
|
gunzip -c "$BACKUP_FILE" | psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" "$DB_NAME"
|
||||||
|
|
||||||
|
# Verify restore
|
||||||
|
echo "Verifying restore..."
|
||||||
|
RECIPE_COUNT=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" "$DB_NAME" -t -c "SELECT COUNT(*) FROM \"Recipe\";")
|
||||||
|
COOKBOOK_COUNT=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" "$DB_NAME" -t -c "SELECT COUNT(*) FROM \"Cookbook\";")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "===== Restore Complete ====="
|
||||||
|
echo "Recipes: $RECIPE_COUNT"
|
||||||
|
echo "Cookbooks: $COOKBOOK_COUNT"
|
||||||
|
echo "Pre-restore backup saved at: $PRE_RESTORE_BACKUP"
|
||||||
|
echo ""
|
||||||
|
echo "If something went wrong, you can restore from the safety backup:"
|
||||||
|
echo " gunzip -c $PRE_RESTORE_BACKUP | psql -h $DB_HOST -U $DB_USER $DB_NAME"
|
||||||
Reference in New Issue
Block a user