fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
358
scripts/maintenance/backup.sh
Executable file
358
scripts/maintenance/backup.sh
Executable file
@@ -0,0 +1,358 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Production Backup Script
|
||||
# Automated backup for database, Vault, and critical files
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/backup.sh [--full|--database-only|--vault-only] [--encrypt]
|
||||
#
|
||||
# Options:
|
||||
# --full Full backup (database + vault + files)
|
||||
# --database-only Database backup only
|
||||
# --vault-only Vault backup only
|
||||
# --encrypt Encrypt backup files with GPG
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
BACKUP_DIR="${PROJECT_ROOT}/../backups"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_PATH="${BACKUP_DIR}/${TIMESTAMP}"
|
||||
|
||||
# Default options
|
||||
BACKUP_TYPE="full"
|
||||
ENCRYPT=false
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--full)
|
||||
BACKUP_TYPE="full"
|
||||
;;
|
||||
--database-only)
|
||||
BACKUP_TYPE="database"
|
||||
;;
|
||||
--vault-only)
|
||||
BACKUP_TYPE="vault"
|
||||
;;
|
||||
--encrypt)
|
||||
ENCRYPT=true
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Colors
|
||||
GREEN="\e[32m"
|
||||
YELLOW="\e[33m"
|
||||
RED="\e[31m"
|
||||
BLUE="\e[34m"
|
||||
RESET="\e[0m"
|
||||
|
||||
# Logging functions
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date +'%H:%M:%S')]${RESET} $1"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}✅ $1${RESET}"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${RESET}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}❌ $1${RESET}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Create backup directory
|
||||
prepare_backup_dir() {
|
||||
log "Preparing backup directory..."
|
||||
|
||||
mkdir -p "$BACKUP_PATH"
|
||||
success "Backup directory created: $BACKUP_PATH"
|
||||
}
|
||||
|
||||
# Backup database
|
||||
backup_database() {
|
||||
log "Backing up database..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Check if database is running
|
||||
if ! docker compose ps db | grep -q "Up"; then
|
||||
error "Database container is not running"
|
||||
fi
|
||||
|
||||
# Dump database
|
||||
local db_backup="${BACKUP_PATH}/database.sql"
|
||||
|
||||
if docker compose exec -T db pg_dump -U postgres michaelschiemer_prod > "$db_backup"; then
|
||||
# Compress
|
||||
gzip -f "$db_backup"
|
||||
local size=$(du -h "${db_backup}.gz" | cut -f1)
|
||||
|
||||
success "Database backup created: database.sql.gz ($size)"
|
||||
|
||||
# Encrypt if requested
|
||||
if [[ "$ENCRYPT" == "true" ]]; then
|
||||
encrypt_file "${db_backup}.gz"
|
||||
fi
|
||||
else
|
||||
error "Database backup failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup Vault secrets
|
||||
backup_vault() {
|
||||
log "Backing up Vault secrets..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
local vault_backup="${BACKUP_PATH}/vault_secrets.sql"
|
||||
|
||||
# Export Vault tables
|
||||
if docker compose exec -T db pg_dump -U postgres michaelschiemer_prod \
|
||||
-t vault_secrets -t vault_audit > "$vault_backup"; then
|
||||
|
||||
# Compress
|
||||
gzip -f "$vault_backup"
|
||||
local size=$(du -h "${vault_backup}.gz" | cut -f1)
|
||||
|
||||
success "Vault backup created: vault_secrets.sql.gz ($size)"
|
||||
|
||||
# Encrypt (recommended for Vault backups)
|
||||
if [[ "$ENCRYPT" == "true" ]]; then
|
||||
encrypt_file "${vault_backup}.gz"
|
||||
else
|
||||
warning "Vault backup is not encrypted - consider using --encrypt"
|
||||
fi
|
||||
else
|
||||
error "Vault backup failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup environment configuration
|
||||
backup_environment() {
|
||||
log "Backing up environment configuration..."
|
||||
|
||||
if [[ -f "$PROJECT_ROOT/.env.production" ]]; then
|
||||
cp "$PROJECT_ROOT/.env.production" "${BACKUP_PATH}/env.production"
|
||||
success "Environment configuration backed up"
|
||||
|
||||
if [[ "$ENCRYPT" == "true" ]]; then
|
||||
encrypt_file "${BACKUP_PATH}/env.production"
|
||||
fi
|
||||
else
|
||||
warning ".env.production not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup storage directory
|
||||
backup_storage() {
|
||||
log "Backing up storage directory..."
|
||||
|
||||
if [[ -d "$PROJECT_ROOT/storage" ]]; then
|
||||
local storage_backup="${BACKUP_PATH}/storage.tar.gz"
|
||||
|
||||
tar -czf "$storage_backup" -C "$PROJECT_ROOT" storage
|
||||
|
||||
local size=$(du -h "$storage_backup" | cut -f1)
|
||||
success "Storage backup created: storage.tar.gz ($size)"
|
||||
|
||||
if [[ "$ENCRYPT" == "true" ]]; then
|
||||
encrypt_file "$storage_backup"
|
||||
fi
|
||||
else
|
||||
warning "Storage directory not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup uploaded files
|
||||
backup_uploads() {
|
||||
log "Backing up uploaded files..."
|
||||
|
||||
if [[ -d "$PROJECT_ROOT/public/uploads" ]]; then
|
||||
local uploads_backup="${BACKUP_PATH}/uploads.tar.gz"
|
||||
|
||||
tar -czf "$uploads_backup" -C "$PROJECT_ROOT/public" uploads
|
||||
|
||||
local size=$(du -h "$uploads_backup" | cut -f1)
|
||||
success "Uploads backup created: uploads.tar.gz ($size)"
|
||||
|
||||
if [[ "$ENCRYPT" == "true" ]]; then
|
||||
encrypt_file "$uploads_backup"
|
||||
fi
|
||||
else
|
||||
log "No uploads directory found (skipping)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Encrypt file with GPG
|
||||
encrypt_file() {
|
||||
local file="$1"
|
||||
|
||||
log "Encrypting $file..."
|
||||
|
||||
# Check if GPG is available
|
||||
if ! command -v gpg &> /dev/null; then
|
||||
warning "GPG not installed - skipping encryption"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Encrypt with symmetric encryption (password-based)
|
||||
if gpg --symmetric --cipher-algo AES256 "$file"; then
|
||||
rm -f "$file" # Remove unencrypted file
|
||||
success "File encrypted: ${file}.gpg"
|
||||
else
|
||||
error "Encryption failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create backup manifest
|
||||
create_manifest() {
|
||||
log "Creating backup manifest..."
|
||||
|
||||
local manifest="${BACKUP_PATH}/MANIFEST.txt"
|
||||
|
||||
{
|
||||
echo "Backup Manifest"
|
||||
echo "==============="
|
||||
echo ""
|
||||
echo "Timestamp: $(date -Iseconds)"
|
||||
echo "Backup Type: $BACKUP_TYPE"
|
||||
echo "Encrypted: $ENCRYPT"
|
||||
echo ""
|
||||
echo "Contents:"
|
||||
echo ""
|
||||
find "$BACKUP_PATH" -type f -exec du -h {} \; | sort -rh
|
||||
echo ""
|
||||
echo "Total Size: $(du -sh "$BACKUP_PATH" | cut -f1)"
|
||||
} > "$manifest"
|
||||
|
||||
success "Backup manifest created"
|
||||
}
|
||||
|
||||
# Cleanup old backups
|
||||
cleanup_old_backups() {
|
||||
log "Cleaning up old backups..."
|
||||
|
||||
# Keep last 7 days of backups
|
||||
find "$BACKUP_DIR" -maxdepth 1 -type d -name "20*" -mtime +7 -exec rm -rf {} \;
|
||||
|
||||
success "Old backups cleaned up (kept last 7 days)"
|
||||
}
|
||||
|
||||
# Verify backup integrity
|
||||
verify_backup() {
|
||||
log "Verifying backup integrity..."
|
||||
|
||||
local all_valid=true
|
||||
|
||||
# Verify gzip files
|
||||
for file in "$BACKUP_PATH"/*.gz; do
|
||||
if [[ -f "$file" ]]; then
|
||||
if gzip -t "$file" 2>/dev/null; then
|
||||
log "✓ $file is valid"
|
||||
else
|
||||
error "✗ $file is corrupted"
|
||||
all_valid=false
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify tar.gz files
|
||||
for file in "$BACKUP_PATH"/*.tar.gz; do
|
||||
if [[ -f "$file" ]]; then
|
||||
if tar -tzf "$file" &>/dev/null; then
|
||||
log "✓ $file is valid"
|
||||
else
|
||||
error "✗ $file is corrupted"
|
||||
all_valid=false
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$all_valid" == "true" ]]; then
|
||||
success "All backup files verified successfully"
|
||||
else
|
||||
error "Some backup files are corrupted"
|
||||
fi
|
||||
}
|
||||
|
||||
# Display backup summary
|
||||
display_summary() {
|
||||
echo ""
|
||||
echo -e "${GREEN}========================================${RESET}"
|
||||
echo -e "${GREEN} Backup Summary${RESET}"
|
||||
echo -e "${GREEN}========================================${RESET}"
|
||||
echo ""
|
||||
echo "📋 Backup Type: $BACKUP_TYPE"
|
||||
echo "⏰ Timestamp: $(date)"
|
||||
echo "📁 Location: $BACKUP_PATH"
|
||||
echo "🔒 Encrypted: $ENCRYPT"
|
||||
echo ""
|
||||
echo "📦 Backup Contents:"
|
||||
echo ""
|
||||
find "$BACKUP_PATH" -type f -exec du -h {} \; | sort -rh | head -10
|
||||
echo ""
|
||||
echo "💾 Total Size: $(du -sh "$BACKUP_PATH" | cut -f1)"
|
||||
echo ""
|
||||
echo "📝 Restoration Commands:"
|
||||
echo ""
|
||||
|
||||
if [[ -f "${BACKUP_PATH}/database.sql.gz" ]]; then
|
||||
echo " Database:"
|
||||
echo " gunzip -c database.sql.gz | docker compose exec -T db psql -U postgres michaelschiemer_prod"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ -f "${BACKUP_PATH}/vault_secrets.sql.gz" ]]; then
|
||||
echo " Vault:"
|
||||
echo " gunzip -c vault_secrets.sql.gz | docker compose exec -T db psql -U postgres michaelschiemer_prod"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ -f "${BACKUP_PATH}/storage.tar.gz" ]]; then
|
||||
echo " Storage:"
|
||||
echo " tar -xzf storage.tar.gz -C /path/to/project"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}========================================${RESET}"
|
||||
}
|
||||
|
||||
# Main backup execution
|
||||
main() {
|
||||
log "🔐 Starting production backup (type: $BACKUP_TYPE)..."
|
||||
echo ""
|
||||
|
||||
prepare_backup_dir
|
||||
|
||||
case "$BACKUP_TYPE" in
|
||||
full)
|
||||
backup_database
|
||||
backup_vault
|
||||
backup_environment
|
||||
backup_storage
|
||||
backup_uploads
|
||||
;;
|
||||
database)
|
||||
backup_database
|
||||
;;
|
||||
vault)
|
||||
backup_vault
|
||||
;;
|
||||
esac
|
||||
|
||||
create_manifest
|
||||
verify_backup
|
||||
cleanup_old_backups
|
||||
display_summary
|
||||
|
||||
success "🎉 Backup completed successfully!"
|
||||
}
|
||||
|
||||
# Run main
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user