Netgrimoire/immich_backup.md
2026-02-14 03:16:21 +00:00

12 KiB
Raw Blame History

title description published date tags editor dateCreated
Immich Backup and Restore Immich backup with Kopia true 2026-02-14T03:16:12.150Z markdown 2026-02-14T03:14:32.594Z

Immich Backup and Recovery Guide

Overview

This document provides comprehensive backup and recovery procedures for Immich photo management server. Following the same proven strategy as our Mailcow backups, we use a two-tier approach combining local component-level backups with Kopia for offsite storage in vaults.

Quick Reference

Common Backup Commands

# Run a manual backup (all components)
/opt/scripts/backup-immich.sh

# List Kopia snapshots
kopia snapshot list --tags immich

# View backup logs
tail -f /var/log/immich-backup.log

Common Restore Commands

# Restore from local backup (interactive)
/opt/immich-backups/immich-restore.sh /opt/immich-backups/immich-YYYYMMDD_HHMMSS/

# Restore from Kopia to new server
kopia snapshot list --tags tier1-backup
kopia restore <snapshot-id> /opt/immich-backups/

# Check container status after restore
docker compose ps
docker compose logs -f

Critical Components to Backup

1. Docker Compose Configuration

  • Location: /opt/immich/docker-compose.yml
  • Purpose: Defines all containers, networks, and volumes
  • Importance: Critical for recreating the exact container configuration

2. Environment Configuration

  • Primary Config: /opt/immich/.env
  • Purpose: Database credentials, upload locations, API keys
  • Importance: Required for proper service initialization

3. PostgreSQL Database

  • Purpose: Contains all metadata, user accounts, albums, sharing settings, face recognition data
  • Docker Volume: immich_postgres
  • Backup Method: pg_dumpall (hot backup, no downtime)

4. Photo/Video Library

  • Purpose: All original photos and videos uploaded by users
  • Docker Volume: immich_upload or immich_library
  • Size: Typically the largest component
  • Critical: This is your actual data - photos cannot be recreated

5. Additional Important Data

  • Thumbnails: Can be regenerated but saves processing time
  • Encoded Video: Transcoded versions, can be regenerated
  • Profile Pictures: User avatars
  • Machine Learning Models: Can be re-downloaded

Backup Strategy

Two-Tier Backup Approach

We use a two-tier approach combining local snapshots with Kopia for offsite storage:

  1. Tier 1 (Local): Component-level backups (database dump + volume archives)
  2. Tier 2 (Offsite): Kopia snapshots the local backups and syncs to vaults

Why This Approach?

  • Best of both worlds: Local backups ensure quick component-level restore, Kopia provides deduplication and offsite protection
  • Component-level restore: Can restore individual components (just database, just photos, etc.)
  • Disaster recovery: Full system restore from Kopia backups on new server
  • Efficient storage: Kopia's deduplication reduces storage needs for offsite copies
  • Proven strategy: Same approach used successfully for Mailcow backups

Backup Frequency

  • Daily: Local Tier 1 backup runs at 2 AM
  • Daily: Kopia Tier 2 snapshot runs immediately after Tier 1
  • Retention (Local): 7 days of local backups
  • Retention (Kopia/Offsite): 30 daily, 12 weekly, 12 monthly

What Gets Backed Up

Each backup creates:

  • database.sql.gz: Complete PostgreSQL dump
  • immich_*.tar.gz: Compressed archives of each Docker volume
  • docker-compose.yml: Container configuration
  • .env: Environment variables and secrets
  • manifest.txt: Backup inventory and sizes
  • checksums.sha256: Integrity verification

Setting Up Immich Backups

Prerequisites

Connect to Kopia Repository:

sudo kopia repository connect server \
  --url=https://192.168.5.10:51516 \
  --override-username=admin \
  --server-cert-fingerprint=YOUR_FINGERPRINT_HERE

Step 1: Configure Backup Location

sudo mkdir -p /opt/immich-backups
sudo chown -R root:root /opt/immich-backups
sudo chmod 755 /opt/immich-backups

Step 2: Install Backup Scripts

sudo mkdir -p /opt/scripts

sudo cp immich-backup.sh /opt/scripts/backup-immich.sh
sudo cp immich-restore.sh /opt/immich-backups/immich-restore.sh

sudo chmod +x /opt/scripts/backup-immich.sh
sudo chmod +x /opt/immich-backups/immich-restore.sh

Step 3: Configure Backup Script

Edit /opt/scripts/backup-immich.sh and verify:

BACKUP_DIR="/opt/immich-backups"
RETENTION_DAYS=7
IMMICH_DIR="/opt/immich"
POSTGRES_CONTAINER="immich_postgres"
PROJECT_NAME="immich"
ENABLE_KOPIA=true

Find your PostgreSQL container:

docker ps | grep postgres

Step 4: Test Manual Backup

sudo /opt/scripts/backup-immich.sh

Verify:

ls -lh /opt/immich-backups/
kopia snapshot list --tags immich

Step 5: Automated Backup with Cron

sudo crontab -e

Add for daily backups at 2 AM:

0 2 * * * /opt/scripts/backup-immich.sh 2>&1 | logger -t immich-backup

Step 6: Configure Kopia Retention

kopia policy set /opt/immich-backups \
  --keep-latest 30 \
  --keep-daily 30 \
  --keep-weekly 12 \
  --keep-monthly 12

kopia policy set /opt/immich \
  --keep-latest 7 \
  --keep-daily 7

Recovery Procedures

Full system restore:

ls -lh /opt/immich-backups/

sudo /opt/immich-backups/immich-restore.sh /opt/immich-backups/immich-YYYYMMDD_HHMMSS/

Database-only restore:

cd /opt/immich
docker compose down
docker compose up -d postgres
sleep 10

BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
gunzip < "${BACKUP_PATH}/database.sql.gz" | docker exec -i immich_postgres psql -U postgres

docker compose down
docker compose up -d

Volume-only restore:

cd /opt/immich
docker compose down

BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
VOLUME_NAME="immich_upload"

docker run --rm \
  -v "${VOLUME_NAME}:/data" \
  -v "${BACKUP_PATH}:/backup" \
  alpine sh -c "cd /data && tar -xzf /backup/${VOLUME_NAME}.tar.gz"

docker compose up -d

Method 2: Complete Server Rebuild

Step 1: Prepare New Server

# Install Docker
curl -fsSL https://get.docker.com | sh
sudo systemctl enable docker
sudo apt install docker-compose-plugin -y

# Install Kopia
curl -s https://kopia.io/signing-key | sudo gpg --dearmor -o /usr/share/keyrings/kopia-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/kopia-keyring.gpg] https://packages.kopia.io/apt/ stable main" | sudo tee /etc/apt/sources.list.d/kopia.list
sudo apt update && sudo apt install kopia -y

# Create directories
sudo mkdir -p /opt/immich /opt/immich-backups /opt/scripts

Step 2: Connect to Kopia

sudo kopia repository connect server \
  --url=https://YOUR_KOPIA_SERVER:51516 \
  --override-username=admin \
  --server-cert-fingerprint=YOUR_FINGERPRINT

kopia repository status

Step 3: Restore Configuration

kopia snapshot list --tags config
kopia restore <config-snapshot-id> /opt/immich/

ls -la /opt/immich/docker-compose.yml
ls -la /opt/immich/.env

Step 4: Restore Backups

kopia snapshot list --tags tier1-backup
kopia restore <snapshot-id> /opt/immich-backups/

ls -la /opt/immich-backups/

Step 5: Run Local Restore

LATEST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1)
sudo /opt/immich-backups/immich-restore.sh "$LATEST_BACKUP"

Step 6: Start and Verify

cd /opt/immich
docker compose pull
docker compose up -d
docker compose logs -f

Verify:

  • All containers running: docker compose ps
  • Web interface accessible: curl -I http://localhost:2283
  • Database accessible: docker compose exec postgres psql -U postgres -c "\l"
  • Photos visible: docker compose exec immich_server ls /usr/src/app/upload/
  • Login and check albums, timeline, sharing

Verification and Testing

Monthly backup verification:

# Verify local backups
ls -lth /opt/immich-backups/ | head -5

# Verify checksums
LATEST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1)
cd "$LATEST_BACKUP" && sha256sum -c checksums.sha256

# Verify Kopia snapshots
kopia snapshot list --tags immich

Backup Monitoring Script

Create /opt/scripts/check-immich-backup.sh:

#!/bin/bash

LAST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1)

if [ -z "$LAST_BACKUP" ]; then
    echo "ERROR: No backups found!"
    exit 1
fi

BACKUP_DATE=$(basename "$LAST_BACKUP" | sed 's/immich-//')
BACKUP_EPOCH=$(date -d "${BACKUP_DATE:0:8} ${BACKUP_DATE:9:2}:${BACKUP_DATE:11:2}:${BACKUP_DATE:13:2}" +%s 2>/dev/null)
NOW=$(date +%s)
AGE_HOURS=$(( ($NOW - $BACKUP_EPOCH) / 3600 ))

if [ $AGE_HOURS -gt 26 ]; then
    echo "WARNING: Last Immich backup is $AGE_HOURS hours old"
    exit 1
else
    echo "OK: Last backup $AGE_HOURS hours ago"
    BACKUP_SIZE=$(du -sh "$LAST_BACKUP" | cut -f1)
    echo "Size: $BACKUP_SIZE"
fi

Add to cron:

chmod +x /opt/scripts/check-immich-backup.sh
sudo crontab -e
0 8 * * * /opt/scripts/check-immich-backup.sh | logger -t immich-backup-check

Disaster Recovery Checklist

  • Confirm scope of failure
  • Access offsite Kopia repository
  • Provision new server with Docker and Kopia
  • Connect to Kopia repository
  • Restore configuration from Kopia
  • Restore backup directory from Kopia
  • Run local restore script
  • Start Immich containers
  • Verify web interface and photo access
  • Update DNS if needed
  • Document issues and lessons learned

Important Notes

  1. Machine Learning Models: Can be excluded from backup (re-download automatically)
  2. Photo Deduplication: Remains intact after restore
  3. Facial Recognition: Stored in database, restored automatically
  4. Permissions: Handled automatically by Docker
  5. Upgrades: Always restore with same or newer Immich version
  6. DNS: Update if restoring to new server IP

Troubleshooting

"Database backup failed"

docker ps | grep postgres
docker compose logs postgres
docker exec immich_postgres pg_dumpall -U postgres | head

"Volume not found"

docker volume ls | grep immich
docker compose up -d --no-start
docker volume ls

"Kopia snapshot fails"

kopia repository status
kopia repository connect server --url=...
kopia snapshot create /opt/immich-backups

"Photos missing after restore"

docker compose exec immich_server ls -lah /usr/src/app/upload/
docker volume inspect immich_upload
docker compose logs -f immich_server

Advanced Topics

Exclude Components to Save Space

Edit backup script to exclude thumbnails/encoded videos:

docker volume ls --filter "name=${PROJECT_NAME}" --format "{{.Name}}" | grep -v "thumbs"

Incremental Backups

# Daily: Database + config only
0 2 * * 1-6 /opt/scripts/backup-immich-db-only.sh

# Weekly: Full backup
0 2 * * 0 /opt/scripts/backup-immich.sh

Backup Notifications

Add to end of backup script:

ADMIN_EMAIL="admin@example.com"
echo "Immich backup completed: ${TIER1_SIZE}" | \
  mail -s "✓ Immich Backup Success" "$ADMIN_EMAIL"

curl -fsS --retry 3 https://hc-ping.com/your-uuid-here

Backup Architecture Notes

Storage Efficiency Example

For a 500GB photo library:

Without Kopia:

  • 7 days × 500GB = 3.5TB local storage

With Two-Tier:

  • Local: 3.5TB (7 days)
  • Kopia vault: ~500GB + (30 × 10GB) = ~800GB
  • Savings: 70-80% in vault storage

Component Restore Priority

Component When to Restore Can Regenerate? Priority
Database Always No Critical
Upload/Library Always No Critical
Thumbnails Optional Yes Medium
Encoded Videos Optional Yes Low
Model Cache Never Yes Low

Additional Resources

Revision History

Date Version Changes
2026-02-13 1.0 Initial documentation - two-tier backup strategy

Last Updated: February 13, 2026
Review Schedule: Quarterly