Backup Script
#!/bin/bash
#
# VPS Backup Script - Backs up to Hetzner Storage Box
# Runs daily via cron, creates daily/weekly/monthly backups
#
set -e
# Configuration
STORAGE_BOX="u522581@u522581.your-storagebox.de"
STORAGE_PORT=23
BACKUP_DIR="/tmp/vps-backup-$$"
DATE=$(date +%Y-%m-%d)
DAY_OF_WEEK=$(date +%u) # 1=Monday, 7=Sunday
DAY_OF_MONTH=$(date +%d)
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[$(date +%H:%M:%S)]${NC} $1"; }
warn() { echo -e "${YELLOW}[$(date +%H:%M:%S)]${NC} $1"; }
error() { echo -e "${RED}[$(date +%H:%M:%S)]${NC} $1"; }
cleanup() {
rm -rf "$BACKUP_DIR"
log "Cleanup complete"
}
trap cleanup EXIT
# Create temp directory
mkdir -p "$BACKUP_DIR"
log "Starting VPS backup - $DATE"
# 1. Backup PostgreSQL
log "Backing up PostgreSQL..."
docker exec postgres pg_dumpall -U postgres > "$BACKUP_DIR/postgres-all-$DATE.sql" 2>/dev/null || warn "PostgreSQL backup failed"
if [ -f "$BACKUP_DIR/postgres-all-$DATE.sql" ]; then
gzip "$BACKUP_DIR/postgres-all-$DATE.sql"
log "PostgreSQL backup: $(du -h $BACKUP_DIR/postgres-all-$DATE.sql.gz | cut -f1)"
fi
# 2. Backup Redis
log "Backing up Redis..."
docker exec redis redis-cli BGSAVE >/dev/null 2>&1 || true
sleep 2
docker cp redis:/data/dump.rdb "$BACKUP_DIR/redis-$DATE.rdb" 2>/dev/null || warn "Redis backup failed"
# 3. Backup Docker configs (compose, .env, Caddyfile)
log "Backing up Docker configs..."
tar -czf "$BACKUP_DIR/docker-configs-$DATE.tar.gz" \
-C /opt/docker \
docker-compose.yml \
.env \
caddy/Caddyfile \
2>/dev/null || warn "Config backup partial"
# 4. Backup n8n data
log "Backing up n8n data..."
tar -czf "$BACKUP_DIR/n8n-data-$DATE.tar.gz" -C /opt/docker n8n/data 2>/dev/null || warn "n8n backup failed"
# 5. Backup KaviCloud data (presets, queue)
log "Backing up KaviCloud data..."
tar -czf "$BACKUP_DIR/kavicloud-data-$DATE.tar.gz" -C /opt/docker kavicloud/data 2>/dev/null || warn "KaviCloud backup failed"
# 6. Backup Portainer data
log "Backing up Portainer..."
tar -czf "$BACKUP_DIR/portainer-data-$DATE.tar.gz" -C /opt/docker portainer/data 2>/dev/null || warn "Portainer backup failed"
# Create combined archive
log "Creating combined backup archive..."
ARCHIVE_NAME="vps-backup-$DATE.tar.gz"
cd "$BACKUP_DIR"
tar -czf "$ARCHIVE_NAME" $(ls | grep -v "$ARCHIVE_NAME") 2>/dev/null || true
ARCHIVE_SIZE=$(du -h "$BACKUP_DIR/$ARCHIVE_NAME" | cut -f1)
log "Archive size: $ARCHIVE_SIZE"
# Upload to Storage Box - Daily
log "Uploading to Storage Box (daily)..."
sftp -P $STORAGE_PORT $STORAGE_BOX << SFTP
put "$BACKUP_DIR/$ARCHIVE_NAME" backups/hetzner-vps/daily/$ARCHIVE_NAME
SFTP
# Weekly backup on Sundays (day 7)
if [ "$DAY_OF_WEEK" = "7" ]; then
log "Creating weekly backup (Sunday)..."
sftp -P $STORAGE_PORT $STORAGE_BOX << SFTP
put "$BACKUP_DIR/$ARCHIVE_NAME" backups/hetzner-vps/weekly/vps-backup-week-$(date +%Y-%W).tar.gz
SFTP
fi
# Monthly backup on the 1st
if [ "$DAY_OF_MONTH" = "01" ]; then
log "Creating monthly backup (1st of month)..."
sftp -P $STORAGE_PORT $STORAGE_BOX << SFTP
put "$BACKUP_DIR/$ARCHIVE_NAME" backups/hetzner-vps/monthly/vps-backup-$(date +%Y-%m).tar.gz
SFTP
fi
log "Backup completed successfully!"
log "Location: Storage Box -> backups/hetzner-vps/daily/$ARCHIVE_NAME"