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.
| Component | Contains | Built-in Backup? |
|---|---|---|
| PostgreSQL Database | Metadata, 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:
| Type | When? | Example Name |
|---|---|---|
| daily | Every day except Sunday/first of month | daily-2026-03-10 |
| weekly | Every Sunday | weekly-2026-03-15 |
| monthly | Every 1st of the month | monthly-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:
| Position | Value | Meaning |
|---|---|---|
| Minute | 0 | At the full hour |
| Hour | 23 | At 11:00 PM |
| Day | * | Every day |
| Month | * | Every month |
| Weekday | 0,3 | Sunday (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!

Support / Donation Link for the Channel
If my posts have been helpful or supported you in any way, I’d truly appreciate your support 🙏
#Immich #PhotoManagement #Photos #PhotoAlbum #Docker #Linux #WSL