--- title: Immich Backup and Restore description: Immich backup with Kopia published: true date: 2026-02-14T03:16:12.150Z tags: editor: markdown dateCreated: 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 ```bash # 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 ```bash # 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 /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: ```bash 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 ```bash 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 ```bash 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: ```bash 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: ```bash docker ps | grep postgres ``` ### Step 4: Test Manual Backup ```bash sudo /opt/scripts/backup-immich.sh ``` Verify: ```bash ls -lh /opt/immich-backups/ kopia snapshot list --tags immich ``` ### Step 5: Automated Backup with Cron ```bash 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 ```bash 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 ### Method 1: Local Restore (Recommended) Full system restore: ```bash ls -lh /opt/immich-backups/ sudo /opt/immich-backups/immich-restore.sh /opt/immich-backups/immich-YYYYMMDD_HHMMSS/ ``` Database-only restore: ```bash 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: ```bash 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 ```bash # 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 ```bash 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 ```bash kopia snapshot list --tags config kopia restore /opt/immich/ ls -la /opt/immich/docker-compose.yml ls -la /opt/immich/.env ``` #### Step 4: Restore Backups ```bash kopia snapshot list --tags tier1-backup kopia restore /opt/immich-backups/ ls -la /opt/immich-backups/ ``` #### Step 5: Run Local Restore ```bash LATEST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1) sudo /opt/immich-backups/immich-restore.sh "$LATEST_BACKUP" ``` #### Step 6: Start and Verify ```bash 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: ```bash # 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`: ```bash #!/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: ```bash 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" ```bash docker ps | grep postgres docker compose logs postgres docker exec immich_postgres pg_dumpall -U postgres | head ``` ### "Volume not found" ```bash docker volume ls | grep immich docker compose up -d --no-start docker volume ls ``` ### "Kopia snapshot fails" ```bash kopia repository status kopia repository connect server --url=... kopia snapshot create /opt/immich-backups ``` ### "Photos missing after restore" ```bash 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: ```bash 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: ```bash 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 - [Immich Official Documentation](https://immich.app/docs) - [Kopia Documentation](https://kopia.io/docs/) - [Docker Volume Backup Best Practices](https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes) ## Revision History | Date | Version | Changes | |------|---------|---------| | 2026-02-13 | 1.0 | Initial documentation - two-tier backup strategy | --- **Last Updated**: February 13, 2026 **Review Schedule**: Quarterly