diff --git a/backup-immich-db.sh b/backup-immich-db.sh new file mode 100755 index 0000000..f7f231a --- /dev/null +++ b/backup-immich-db.sh @@ -0,0 +1,147 @@ +#!/bin/bash + +# Immich Postgres Database Backup Script +# This script creates a # Check if the Postgres container exists and is running +echo "Checking postgres container status..." +if ! docker ps -q --filter "name=immich_postgres" | grep -q .; then + echo "Error: immich_postgres container is not running. Cannot proceed with backup." + exit 1 +fi + +echo "Taking database backup using pg_dumpall as recommended by Immich documentation..." +# Use pg_dumpall with recommended flags: --clean and --if-exists +docker exec -t immich_postgres pg_dumpall \ + --clean \ + --if-exists \ + --username="${DB_USERNAME}" \ + > "${BACKUP_PATH}" 2>/tmp/pg_dumpall_error.log + +# Check if the dump was successful the Immich Postgres database +# Based on recommendations from https://immich.app/docs/administration/backup-and-restore/ + +# Set up error handling +set -e + +# Function to ensure server is unpaused even if script fails +cleanup() { + local exit_code=$? + echo "Running cleanup..." + + # Check if immich_server is paused and unpause it if needed + if [ "${IMMICH_SERVER_RUNNING:-true}" = true ] && docker inspect --format='{{.State.Status}}' immich_server 2>/dev/null | grep -q "paused"; then + echo "Unpausing immich_server container during cleanup..." + docker unpause immich_server 2>/dev/null || true + fi + + if [ $exit_code -ne 0 ]; then + echo "Script failed with exit code $exit_code" + fi + + exit $exit_code +} + +# Set up trap to call cleanup function on script exit (normal or error) +trap cleanup EXIT SIGINT SIGTERM + +# Load environment variables from the .env file +ENV_FILE="$(dirname "$0")/.env" +if [ -f "$ENV_FILE" ]; then + echo "Loading environment variables from $ENV_FILE" + source "$ENV_FILE" +else + echo "Error: .env file not found in $(dirname "$0")" + exit 1 +fi + +# Verify required environment variables are set +if [ -z "$DB_USERNAME" ] || [ -z "$DB_DATABASE_NAME" ]; then + echo "Error: Required environment variables (DB_USERNAME, DB_DATABASE_NAME) not found in .env file" + exit 1 +fi + +# Initialize container status variables +IMMICH_SERVER_RUNNING=true + +# Create backup directory if it doesn't exist +BACKUP_DIR="$(dirname "$0")/database_backups" +mkdir -p "$BACKUP_DIR" + +# Generate timestamp for the backup filename +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +BACKUP_FILENAME="immich_db_backup_${TIMESTAMP}.sql" +BACKUP_PATH="${BACKUP_DIR}/${BACKUP_FILENAME}" + +echo "Starting backup of Immich Postgres database..." +echo "Using database settings from .env file:" +echo " - Database: ${DB_DATABASE_NAME}" +echo " - Username: ${DB_USERNAME}" +echo " - Container: immich_postgres" + +# Check if the Immich server container exists and is running +echo "Checking immich_server container status..." +if docker ps -q --filter "name=immich_server" | grep -q .; then + echo "Pausing immich_server container to minimize database writes..." + if ! docker pause immich_server; then + echo "Failed to pause immich_server container." + # Continue with backup instead of exiting + fi +else + echo "Note: immich_server container not found or not running. Continuing with backup anyway." + # Set a flag so we don't try to unpause it later + IMMICH_SERVER_RUNNING=false +fi + +echo "Taking database backup using pg_dumpall as recommended by Immich documentation..." +# Use pg_dumpall with recommended flags: --clean and --if-exists +docker exec -t immich_postgres pg_dumpall \ + --clean \ + --if-exists \ + --username="${DB_USERNAME}" \ + > "${BACKUP_PATH}" + +# Check if the dump was successful +if [ $? -ne 0 ] || [ ! -s "${BACKUP_PATH}" ]; then + echo "Error: Database backup failed or created an empty file." + if [ -f "/tmp/pg_dumpall_error.log" ]; then + echo "Error details:" + cat /tmp/pg_dumpall_error.log + rm /tmp/pg_dumpall_error.log + fi + exit 1 +fi + +# Clean up error log if it exists +rm -f /tmp/pg_dumpall_error.log + +# Compress the backup file +echo "Compressing backup file..." +if ! gzip -f "${BACKUP_PATH}"; then + echo "Warning: Failed to compress backup file." +fi + +# Resume the Immich server only if it was running and we paused it +if [ "${IMMICH_SERVER_RUNNING:-true}" = true ]; then + echo "Resuming immich_server container..." + if ! docker unpause immich_server 2>/dev/null; then + echo "Note: No need to unpause immich_server container." + fi +fi + +echo "Backup completed successfully!" +echo "Backup saved to: ${BACKUP_PATH}.gz" + +# Optional: List backups and show total size +echo -e "\nBackup information:" +find "${BACKUP_DIR}" -name "*.gz" | wc -l | xargs echo "Total number of backups:" +du -sh "${BACKUP_DIR}" | cut -f1 | xargs echo "Total backup size:" + +# Optional: Check if backup file is smaller than expected (potential warning) +BACKUP_SIZE=$(du -k "${BACKUP_PATH}.gz" | cut -f1) +if [ ${BACKUP_SIZE} -lt 100 ]; then + echo "Warning: Backup file is smaller than expected (${BACKUP_SIZE}KB). Please verify its integrity." +fi + +# Optional: Remove backups older than 30 days +# find "${BACKUP_DIR}" -name "immich_db_backup_*.sql.gz" -mtime +30 -delete + +echo -e "\nTo restore this backup, follow the instructions at: https://immich.app/docs/administration/backup-and-restore/"