You are currently viewing Immich Backup Automation Script | Full Automatic Backup

Immich Backup Automation Script | Full Automatic Backup

Immich Backup Automation Script, the full Immich backup with all data including photos and videos. Although Immich offers integrated backup functions, these only back up the database – not your valuable photos and videos! For a truly complete backup, you need both.

ComponentContainsBuilt-in Backup?
PostgreSQL DatabaseMetadata, albums, face recognition, tags✅ Yes (daily dumps)
Media (Photos/Videos)Your original files❌ No

The golden rule: Back up the database first, then the media. This avoids “dead links” during restoration – photos that exist in the database but are physically missing. The time gap between both backups should be minimal.

Our script does exactly that: First the database, then the media – automatically, reliably, and space-efficiently.

Video: Immich Backup Automation Script | Full Automatic Backup

Language: 🇩🇪|🇬🇧
☝️ Use YouTube subtitles for all languages.

Installing rsync on Linux

# Install rsync (Ubuntu/Debian)
sudo apt update
sudo apt install rsync -y

# Verify installation
rsync --version

Script Description

The script is located in your docker-compose.yml directory (e.g., /mnt/d/Visual Edit/immich/backup_immich.sh). The main configuration areas:

Configuration (adjust to your needs!)

# ---------- CONFIGURATION ----------
SOURCE="/mnt/d/Visual Edit/immich/medien/"
DEST_BASE="/mnt/d/backups/immich"
LOG="$DEST_BASE/backup.log"

# Retention period (in days)
KEEP_DAILY=7      # Keep daily backups for 7 days
KEEP_WEEKLY=30    # Keep weekly backups for 30 days (approx. 4 weeks)
KEEP_MONTHLY=365  # Keep monthly backups for 365 days (1 year)

# PostgreSQL Container
DB_CONTAINER="immich_postgres"
DB_USER="postgres"

Intelligent Backup Types

The script automatically distinguishes:

TypeWhen?Example Name
dailyEvery day except Sunday/first of monthdaily-2026-03-10
weeklyEvery Sundayweekly-2026-03-15
monthlyEvery 1st of the monthmonthly-2026-04-01

The Complete Backup Script

#!/bin/bash

# ==============================================
# IMMICH BACKUP SCRIPT with intelligent rotation
# ==============================================

# ---------- CONFIGURATION ----------
SOURCE="/mnt/d/Visual Edit/immich/medien/"
DEST_BASE="/mnt/d/backups/immich"
LOG="$DEST_BASE/backup.log"

# Retention period (in days)
KEEP_DAILY=7      # Keep daily backups for 7 days
KEEP_WEEKLY=30    # Keep weekly backups for 30 days
KEEP_MONTHLY=365  # Keep monthly backups for 365 days

# PostgreSQL Container
DB_CONTAINER="immich_postgres"
DB_USER="postgres"
DB_NAME="immich"

# ---------- GET DATE ----------
DATE=$(date +%Y-%m-%d)
YEAR=$(date +%Y)
MONTH=$(date +%m)
DAY=$(date +%d)
DOW=$(date +%u)   # 1=Monday, 7=Sunday

# ---------- DETERMINE BACKUP TYPE ----------
if [ "$DAY" == "01" ]; then
    BACKUP_TYPE="monthly"
    BACKUP_NAME="monthly-$YEAR-$MONTH"
elif [ "$DOW" == "7" ]; then
    BACKUP_TYPE="weekly"
    BACKUP_NAME="weekly-$YEAR-$MONTH-$DAY"
else
    BACKUP_TYPE="daily"
    BACKUP_NAME="daily-$DATE"
fi

# ---------- SET PATHS ----------
DEST="$DEST_BASE/$BACKUP_NAME"
LATEST_LINK="$DEST_BASE/latest"
DB_BACKUP_DIR="$DEST/database"
DB_BACKUP_FILE="$DB_BACKUP_DIR/immich-db-$DATE.sql"

# ---------- LOGGING FUNCTION ----------
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG"
}

# ========== MAIN PROGRAM ==========
mkdir -p "$DEST_BASE"
log "========================================="
log "BACKUP STARTED - Type: $BACKUP_TYPE"
log "========================================="

# Checks
if [ ! -d "$SOURCE" ]; then
    log "ERROR: Source $SOURCE does not exist!"
    exit 1
fi

# Check Docker container
if ! docker ps | grep -q "$DB_CONTAINER"; then
    log "WARNING: PostgreSQL container $DB_CONTAINER is not running"
    DB_AVAILABLE=false
else
    DB_AVAILABLE=true
fi

# Create target directories
mkdir -p "$DEST"
mkdir -p "$DB_BACKUP_DIR"

# ========== DATABASE BACKUP ==========
if [ "$DB_AVAILABLE" = true ]; then
    log "Starting database backup..."
    docker exec "$DB_CONTAINER" pg_dumpall -U "$DB_USER" > "$DB_BACKUP_FILE" 2>> "$LOG"

    if [ $? -eq 0 ]; then
        gzip -f "$DB_BACKUP_FILE"
        log "✅ Database backup successful: $DB_BACKUP_FILE.gz"
        DB_SIZE=$(du -h "$DB_BACKUP_FILE.gz" | cut -f1)
        log "   Size: $DB_SIZE"
    else
        log "❌ ERROR: Database backup failed!"
    fi
else
    log "⚠️ Database backup skipped"
fi

# ========== PHOTO BACKUP WITH HARDLINKS ==========
log "Starting rsync for photos (hardlink mode)..."

if [ -d "$LATEST_LINK" ]; then
    rsync -a --delete --quiet \
          --link-dest="$LATEST_LINK" \
          "$SOURCE" "$DEST/medien/" >> "$LOG" 2>&1
else
    rsync -a "$SOURCE" "$DEST/medien/" >> "$LOG" 2>&1
fi

if [ $? -eq 0 ]; then
    log "✅ rsync completed successfully"
    rm -f "$LATEST_LINK"
    ln -s "$DEST" "$LATEST_LINK"
    log "✅ Latest link updated"

    if [ -d "$DEST/medien" ]; then
        PHOTO_SIZE=$(du -sh "$DEST/medien" | cut -f1)
        log "   Photos size: $PHOTO_SIZE"
    fi
else
    log "❌ ERROR: rsync failed!"
    exit 1
fi

# ========== CLEANUP OLD BACKUPS ==========
log "Starting cleanup of old backups..."
cd "$DEST_BASE"

find . -maxdepth 1 -type d -name "daily-*" -mtime +$KEEP_DAILY 2>/dev/null | while read dir; do
    log "Deleting old daily backup: $dir"
    rm -rf "$dir"
done

find . -maxdepth 1 -type d -name "weekly-*" -mtime +$KEEP_WEEKLY 2>/dev/null | while read dir; do
    log "Deleting old weekly backup: $dir"
    rm -rf "$dir"
done

find . -maxdepth 1 -type d -name "monthly-*" -mtime +$KEEP_MONTHLY 2>/dev/null | while read dir; do
    log "Deleting old monthly backup: $dir"
    rm -rf "$dir"
done

# ========== SUMMARY ==========
log "========================================="
log "BACKUP COMPLETED"
log "Type: $BACKUP_TYPE"
log "Target: $DEST"
log "  - Database: $([ "$DB_AVAILABLE" = true ] && echo "✅" || echo "⚠️ skipped")"
log "  - Photos: ✅ (hardlinks active)"
log "Retention: $KEEP_DAILY/$KEEP_WEEKLY/$KEEP_MONTHLY days"
log "========================================="

Hinweis: Vergesse nicht die Datei backup_immich.sh als “ausführbar” zu berechtigen:

chmod +x backup_immich.sh

Otherwise, the script cannot be started!

Crontab Setup (Linux/WSL)

Important: Windows Task Scheduler doesn’t work reliably here because WSL instances have issues in background mode. The solution is Cron within your WSL environment:

# Open crontab
crontab -e

# Add the following line (Wed+Sun at 11:00 PM):
0 23 * * 0,3 /mnt/d/Visual\ Edit/immich/backup_immich.sh >> /home/mike/cron.log 2>&1

# Save and exit

Crontab Explanation:

PositionValueMeaning
Minute0At the full hour
Hour23At 11:00 PM
Day*Every day
Month*Every month
Weekday0,3Sunday (0) and Wednesday (3)

Testing Cron:

# Check if Cron is running
sudo service cron status

# Display crontab
crontab -l

# Check logs
cat /home/mike/cron.log

Summary

Your backup system now:

  • Complete (database + photos)
  • Automatic (Wed+Sun at 11:00 PM)
  • Space-efficient (hardlinks)
  • Versioned (daily/weekly/monthly)
  • Restorable (every snapshot)

Enjoy the peace of mind – your photos are now truly protected!


Donate Bild

Support / Donation Link for the Channel
If my posts have been helpful or supported you in any way, I’d truly appreciate your support 🙏

PayPal Link
Bank transfer, Bitcoin and Lightning


#Immich #PhotoManagement #Photos #PhotoAlbum  #Docker #Linux #WSL

Leave a Reply