Files
backup/scripts/backup.sh
2026-02-02 23:44:24 +01:00

147 lines
3.8 KiB
Bash

#!/bin/bash
set -e
BACKUP_TARGET="${BACKUP_TARGET:-/backups}"
BACKUP_PASSWORD="${BACKUP_PASSWORD:-}"
BACKUP_RETENTION="${BACKUP_RETENTION:-7d}"
# Paths to backup
BACKUP_PATHS=(
"/data/postgres"
"/data/redis"
"/data/duckdb"
"/data/loki"
"/data/caddy"
"/personalities"
"/workspaces"
)
# Validate password
if [ -z "$BACKUP_PASSWORD" ]; then
echo "ERROR: BACKUP_PASSWORD is required"
exit 1
fi
# Setup restic environment
export RESTIC_PASSWORD="$BACKUP_PASSWORD"
export RESTIC_REPOSITORY="$BACKUP_TARGET"
# Setup cloud credentials if provided
[ -n "$BACKUP_S3_ACCESS_KEY" ] && export AWS_ACCESS_KEY_ID="$BACKUP_S3_ACCESS_KEY"
[ -n "$BACKUP_S3_SECRET_KEY" ] && export AWS_SECRET_ACCESS_KEY="$BACKUP_S3_SECRET_KEY"
[ -n "$BACKUP_B2_ACCOUNT_ID" ] && export B2_ACCOUNT_ID="$BACKUP_B2_ACCOUNT_ID"
[ -n "$BACKUP_B2_ACCOUNT_KEY" ] && export B2_ACCOUNT_KEY="$BACKUP_B2_ACCOUNT_KEY"
START_TIME=$(date +%s)
echo "=== VibeStack Backup ==="
echo "Time: $(date -Iseconds)"
echo "Target: $BACKUP_TARGET"
echo ""
# Pre-backup: dump PostgreSQL if running
pre_backup_postgres() {
if command -v pg_dump &>/dev/null && [ -d "/data/postgres" ]; then
echo "Creating PostgreSQL dump..."
# Source postgres env if available
[ -f /run/vibestack/postgres.env ] && source /run/vibestack/postgres.env
local pg_user="${POSTGRES_USER:-vibestack}"
local pg_db="${POSTGRES_DB:-vibestack}"
local dump_file="/data/postgres/backup.sql"
if pg_dump -U "$pg_user" -d "$pg_db" -f "$dump_file" 2>/dev/null; then
echo " PostgreSQL dump created: $dump_file"
else
echo " PostgreSQL dump skipped (database not running or accessible)"
fi
fi
}
# Pre-backup: trigger Redis BGSAVE if running
pre_backup_redis() {
if command -v redis-cli &>/dev/null; then
echo "Triggering Redis BGSAVE..."
# Source redis env if available
[ -f /run/vibestack/redis.env ] && source /run/vibestack/redis.env
local redis_pass="${REDIS_PASSWORD:-}"
local auth_args=""
[ -n "$redis_pass" ] && auth_args="-a $redis_pass"
if redis-cli $auth_args BGSAVE 2>/dev/null; then
# Wait for save to complete
sleep 2
echo " Redis BGSAVE triggered"
else
echo " Redis BGSAVE skipped (not running)"
fi
fi
}
# Run pre-backup hooks
echo "Running pre-backup hooks..."
pre_backup_postgres
pre_backup_redis
echo ""
# Build list of paths that exist
existing_paths=()
for path in "${BACKUP_PATHS[@]}"; do
if [ -e "$path" ]; then
existing_paths+=("$path")
echo "Including: $path"
fi
done
# Allow overriding with specific path
if [ -n "$1" ] && [ -e "$1" ]; then
existing_paths=("$1")
echo "Backing up only: $1"
fi
if [ ${#existing_paths[@]} -eq 0 ]; then
echo "WARNING: No paths to backup"
exit 0
fi
echo ""
echo "Starting backup..."
# Run restic backup
BACKUP_OUTPUT=$(restic backup "${existing_paths[@]}" --json 2>&1 | tail -1)
# Parse results
SNAPSHOT_ID=$(echo "$BACKUP_OUTPUT" | jq -r '.snapshot_id // empty' 2>/dev/null || echo "")
BYTES_ADDED=$(echo "$BACKUP_OUTPUT" | jq -r '.data_added // 0' 2>/dev/null || echo "0")
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo ""
echo "Backup complete!"
echo " Snapshot: ${SNAPSHOT_ID:-unknown}"
echo " Duration: ${DURATION}s"
echo " Data added: $BYTES_ADDED bytes"
# Apply retention policy
echo ""
echo "Applying retention policy: keep within $BACKUP_RETENTION..."
restic forget --keep-within "$BACKUP_RETENTION" --prune
# Write status
cat > /run/vibestack/backup-status.json << EOF
{
"status": "idle",
"last_backup": "$(date -Iseconds)",
"last_status": "success",
"snapshot_id": "$SNAPSHOT_ID",
"duration_seconds": $DURATION,
"bytes_added": $BYTES_ADDED
}
EOF
echo ""
echo "=== Backup Complete ==="