From ac476cf40833f8a1c227bbdaf9aa7cae0fb27eec Mon Sep 17 00:00:00 2001 From: Peter Wood Date: Thu, 22 May 2025 13:15:16 -0400 Subject: [PATCH] Add Immich Postgres database backup script and update cocker compose config based on (https://github.com/immich-app/immich/discussions/18429) --- immich/backup_immich_db.sh | 147 +++++++++++++++++++++++++++++++++++++ immich/docker-compose.yml | 17 +---- 2 files changed, 150 insertions(+), 14 deletions(-) create mode 100755 immich/backup_immich_db.sh diff --git a/immich/backup_immich_db.sh b/immich/backup_immich_db.sh new file mode 100755 index 0000000..f7f231a --- /dev/null +++ b/immich/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/" diff --git a/immich/docker-compose.yml b/immich/docker-compose.yml index 518f99a..c4a8879 100644 --- a/immich/docker-compose.yml +++ b/immich/docker-compose.yml @@ -51,28 +51,17 @@ services: restart: always database: container_name: immich_postgres - image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} POSTGRES_DB: ${DB_DATABASE_NAME} POSTGRES_INITDB_ARGS: --data-checksums + # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs + DB_STORAGE_TYPE: 'HDD' volumes: # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file - ${DB_DATA_LOCATION}:/var/lib/postgresql/data - healthcheck: - test: pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || - exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" - --username="$${POSTGRES_USER}" --tuples-only --no-align - --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM - pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ - "$$Chksum" = '0' ] || exit 1 - interval: 5m - start_interval: 30s - start_period: 5m - command: postgres -c shared_preload_libraries=vectors.so -c - 'search_path="$$user", public, vectors' -c logging_collector=on -c - max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on restart: always volumes: model-cache: null