docs: update immich_backup
This commit is contained in:
parent
0df4b2965e
commit
7aa392c5b9
1 changed files with 507 additions and 237 deletions
744
immich_backup.md
744
immich_backup.md
|
|
@ -2,7 +2,7 @@
|
||||||
title: Immich Backup and Restore
|
title: Immich Backup and Restore
|
||||||
description: Immich backup with Kopia
|
description: Immich backup with Kopia
|
||||||
published: true
|
published: true
|
||||||
date: 2026-02-14T03:16:12.150Z
|
date: 2026-02-14T03:34:42.017Z
|
||||||
tags:
|
tags:
|
||||||
editor: markdown
|
editor: markdown
|
||||||
dateCreated: 2026-02-14T03:14:32.594Z
|
dateCreated: 2026-02-14T03:14:32.594Z
|
||||||
|
|
@ -12,7 +12,7 @@ dateCreated: 2026-02-14T03:14:32.594Z
|
||||||
|
|
||||||
## Overview
|
## 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.
|
This document provides comprehensive backup and recovery procedures for Immich photo server. Since Immich's data is stored on standard filesystems (not ZFS or BTRFS), snapshots are not available and we rely on Immich's native backup approach combined with Kopia for offsite storage in vaults.
|
||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
|
|
@ -22,6 +22,10 @@ This document provides comprehensive backup and recovery procedures for Immich p
|
||||||
# Run a manual backup (all components)
|
# Run a manual backup (all components)
|
||||||
/opt/scripts/backup-immich.sh
|
/opt/scripts/backup-immich.sh
|
||||||
|
|
||||||
|
# Backup just the database
|
||||||
|
docker exec -t immich_postgres pg_dump --clean --if-exists \
|
||||||
|
--dbname=immich --username=postgres | gzip > "/opt/immich-backups/dump.sql.gz"
|
||||||
|
|
||||||
# List Kopia snapshots
|
# List Kopia snapshots
|
||||||
kopia snapshot list --tags immich
|
kopia snapshot list --tags immich
|
||||||
|
|
||||||
|
|
@ -32,8 +36,9 @@ tail -f /var/log/immich-backup.log
|
||||||
### Common Restore Commands
|
### Common Restore Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Restore from local backup (interactive)
|
# Restore database from backup
|
||||||
/opt/immich-backups/immich-restore.sh /opt/immich-backups/immich-YYYYMMDD_HHMMSS/
|
gunzip < /opt/immich-backups/immich-YYYYMMDD_HHMMSS/dump.sql.gz | \
|
||||||
|
docker exec -i immich_postgres psql --username=postgres --dbname=immich
|
||||||
|
|
||||||
# Restore from Kopia to new server
|
# Restore from Kopia to new server
|
||||||
kopia snapshot list --tags tier1-backup
|
kopia snapshot list --tags tier1-backup
|
||||||
|
|
@ -46,284 +51,575 @@ docker compose logs -f
|
||||||
|
|
||||||
## Critical Components to Backup
|
## Critical Components to Backup
|
||||||
|
|
||||||
### 1. Docker Compose Configuration
|
### 1. Docker Compose File
|
||||||
- **Location**: `/opt/immich/docker-compose.yml`
|
- **Location**: `/opt/immich/docker-compose.yml` (or your installation path)
|
||||||
- **Purpose**: Defines all containers, networks, and volumes
|
- **Purpose**: Defines all containers, networks, and volumes
|
||||||
- **Importance**: Critical for recreating the exact container configuration
|
- **Importance**: Critical for recreating the exact container configuration
|
||||||
|
|
||||||
### 2. Environment Configuration
|
### 2. Configuration Files
|
||||||
- **Primary Config**: `/opt/immich/.env`
|
- **Primary Config**: `/opt/immich/.env`
|
||||||
- **Purpose**: Database credentials, upload locations, API keys
|
- **Purpose**: Database credentials, upload locations, timezone settings
|
||||||
- **Importance**: Required for proper service initialization
|
- **Importance**: Required for proper service initialization
|
||||||
|
|
||||||
### 3. PostgreSQL Database
|
### 3. Database
|
||||||
- **Purpose**: Contains all metadata, user accounts, albums, sharing settings, face recognition data
|
- **PostgreSQL Data**: Contains all metadata, user accounts, albums, sharing settings, face recognition data, timeline information
|
||||||
- **Docker Volume**: `immich_postgres`
|
- **Container**: `immich_postgres`
|
||||||
- **Backup Method**: `pg_dumpall` (hot backup, no downtime)
|
- **Database Name**: `immich` (default)
|
||||||
|
- **User**: `postgres` (default)
|
||||||
|
- **Backup Method**: `pg_dump` (official Immich recommendation)
|
||||||
|
|
||||||
### 4. Photo/Video Library
|
### 4. Photo/Video Library
|
||||||
- **Purpose**: All original photos and videos uploaded by users
|
- **Upload Storage**: All original photos and videos uploaded by users
|
||||||
- **Docker Volume**: `immich_upload` or `immich_library`
|
- **Location**: `/srv/immich/library` (per your .env UPLOAD_LOCATION)
|
||||||
- **Size**: Typically the largest component
|
- **Size**: Typically the largest component
|
||||||
- **Critical**: This is your actual data - photos cannot be recreated
|
- **Critical**: This is your actual data - photos cannot be recreated
|
||||||
|
|
||||||
### 5. Additional Important Data
|
### 5. Additional Important Data
|
||||||
- **Thumbnails**: Can be regenerated but saves processing time
|
- **Model Cache**: Docker volume `immich_model-cache` (machine learning models, can be re-downloaded)
|
||||||
- **Encoded Video**: Transcoded versions, can be regenerated
|
- **External Paths**: `/export/photos` and `/srv/NextCloud-AIO` (mounted as read-only in your setup)
|
||||||
- **Profile Pictures**: User avatars
|
|
||||||
- **Machine Learning Models**: Can be re-downloaded
|
|
||||||
|
|
||||||
## Backup Strategy
|
## Backup Strategy
|
||||||
|
|
||||||
### Two-Tier Backup Approach
|
### Two-Tier Backup Approach
|
||||||
|
|
||||||
We use a **two-tier approach** combining local snapshots with Kopia for offsite storage:
|
We use a **two-tier approach** combining Immich's native backup method with Kopia for offsite storage:
|
||||||
|
|
||||||
1. **Tier 1 (Local)**: Component-level backups (database dump + volume archives)
|
1. **Tier 1 (Local)**: Immich database dump + library backup creates consistent, component-level backups
|
||||||
2. **Tier 2 (Offsite)**: Kopia snapshots the local backups and syncs to vaults
|
2. **Tier 2 (Offsite)**: Kopia snapshots the local backups and syncs to vaults
|
||||||
|
|
||||||
#### Why This Approach?
|
#### Why This Approach?
|
||||||
|
|
||||||
- **Best of both worlds**: Local backups ensure quick component-level restore, Kopia provides deduplication and offsite protection
|
- **Best of both worlds**: Native database dump ensures Immich-specific consistency, Kopia provides deduplication and offsite protection
|
||||||
- **Component-level restore**: Can restore individual components (just database, just photos, etc.)
|
- **Component-level restore**: Can restore individual components (just database, just library, etc.)
|
||||||
- **Disaster recovery**: Full system restore from Kopia backups on new server
|
- **Disaster recovery**: Full system restore from Kopia backups on new server
|
||||||
- **Efficient storage**: Kopia's deduplication reduces storage needs for offsite copies
|
- **Efficient storage**: Kopia's deduplication reduces storage needs for offsite copies
|
||||||
- **Proven strategy**: Same approach used successfully for Mailcow backups
|
|
||||||
|
|
||||||
#### Backup Frequency
|
#### Backup Frequency
|
||||||
- **Daily**: Local Tier 1 backup runs at 2 AM
|
- **Daily**: Immich backup runs at 2 AM
|
||||||
- **Daily**: Kopia Tier 2 snapshot runs immediately after Tier 1
|
- **Daily**: Kopia snapshot of backups runs at 3 AM
|
||||||
- **Retention (Local)**: 7 days of local backups
|
- **Retention (Local)**: 7 days of Immich backups (managed by script)
|
||||||
- **Retention (Kopia/Offsite)**: 30 daily, 12 weekly, 12 monthly
|
- **Retention (Kopia/Offsite)**: 30 daily, 12 weekly, 12 monthly
|
||||||
|
|
||||||
### What Gets Backed Up
|
### Immich Native Backup Method
|
||||||
|
|
||||||
Each backup creates:
|
Immich's official backup approach uses `pg_dump` for the database:
|
||||||
- **database.sql.gz**: Complete PostgreSQL dump
|
- Uses `pg_dump` with `--clean --if-exists` flags for consistent database dumps
|
||||||
- **immich_*.tar.gz**: Compressed archives of each Docker volume
|
- Hot backup without stopping PostgreSQL
|
||||||
- **docker-compose.yml**: Container configuration
|
- Produces compressed `.sql.gz` files
|
||||||
- **.env**: Environment variables and secrets
|
- Database remains available during backup
|
||||||
- **manifest.txt**: Backup inventory and sizes
|
|
||||||
- **checksums.sha256**: Integrity verification
|
**Key Features:**
|
||||||
|
- No downtime required
|
||||||
|
- Consistent point-in-time snapshot
|
||||||
|
- Standard PostgreSQL format (portable across systems)
|
||||||
|
- Can be restored to any PostgreSQL instance
|
||||||
|
|
||||||
## Setting Up Immich Backups
|
## Setting Up Immich Backups
|
||||||
|
|
||||||
### Prerequisites
|
### Prereq:
|
||||||
|
Make sure you are connected to the repository,
|
||||||
|
|
||||||
Connect to Kopia Repository:
|
```bash
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo kopia repository connect server \
|
sudo kopia repository connect server \
|
||||||
--url=https://192.168.5.10:51516 \
|
--url=https://192.168.5.10:51516 \
|
||||||
--override-username=admin \
|
--override-username=admin \
|
||||||
--server-cert-fingerprint=YOUR_FINGERPRINT_HERE
|
--server-cert-fingerprint=696a4999f594b5273a174fd7cab677d8dd1628f9b9d27e557daa87103ee064b2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 1: Configure Backup Location
|
#### Step 1: Configure Backup Location
|
||||||
|
|
||||||
|
Set the backup destination:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /opt/immich-backups
|
# Create the backup directory
|
||||||
sudo chown -R root:root /opt/immich-backups
|
mkdir -p /opt/immich-backups
|
||||||
sudo chmod 755 /opt/immich-backups
|
chown -R root:root /opt/immich-backups
|
||||||
|
chmod 755 /opt/immich-backups
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2: Install Backup Scripts
|
#### Step 2: Manual Backup Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /opt/scripts
|
cd /opt/immich
|
||||||
|
|
||||||
sudo cp immich-backup.sh /opt/scripts/backup-immich.sh
|
# Backup database using Immich's recommended method
|
||||||
sudo cp immich-restore.sh /opt/immich-backups/immich-restore.sh
|
docker exec -t immich_postgres pg_dump \
|
||||||
|
--clean \
|
||||||
|
--if-exists \
|
||||||
|
--dbname=immich \
|
||||||
|
--username=postgres \
|
||||||
|
| gzip > "/opt/immich-backups/dump.sql.gz"
|
||||||
|
|
||||||
sudo chmod +x /opt/scripts/backup-immich.sh
|
# Backup library (photos/videos)
|
||||||
sudo chmod +x /opt/immich-backups/immich-restore.sh
|
tar -czf /opt/immich-backups/library.tar.gz -C /srv/immich library
|
||||||
|
|
||||||
|
# Backup configuration files
|
||||||
|
cp docker-compose.yml /opt/immich-backups/
|
||||||
|
cp .env /opt/immich-backups/
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: Configure Backup Script
|
**What gets created:**
|
||||||
|
- Backup directory: `/opt/immich-backups/immich-YYYY-MM-DD-HH-MM-SS/`
|
||||||
|
- Contains: `dump.sql.gz` (database), `library.tar.gz` (photos), config files
|
||||||
|
|
||||||
Edit `/opt/scripts/backup-immich.sh` and verify:
|
#### Step 3: Automated Backup Script
|
||||||
|
|
||||||
|
Create `/opt/scripts/backup-immich.sh`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
BACKUP_DIR="/opt/immich-backups"
|
#!/bin/bash
|
||||||
RETENTION_DAYS=7
|
|
||||||
|
# Immich Automated Backup Script
|
||||||
|
# This creates Immich backups, then snapshots them with Kopia for offsite storage
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
|
||||||
|
LOG_FILE="/var/log/immich-backup.log"
|
||||||
IMMICH_DIR="/opt/immich"
|
IMMICH_DIR="/opt/immich"
|
||||||
|
BACKUP_DIR="/opt/immich-backups"
|
||||||
|
KEEP_DAYS=7
|
||||||
|
|
||||||
|
# Database credentials from .env
|
||||||
|
DB_USERNAME="postgres"
|
||||||
|
DB_DATABASE_NAME="immich"
|
||||||
POSTGRES_CONTAINER="immich_postgres"
|
POSTGRES_CONTAINER="immich_postgres"
|
||||||
PROJECT_NAME="immich"
|
|
||||||
ENABLE_KOPIA=true
|
echo "[${BACKUP_DATE}] ========================================" | tee -a "$LOG_FILE"
|
||||||
|
echo "[${BACKUP_DATE}] Starting Immich backup process" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Step 1: Run Immich database backup using official method
|
||||||
|
echo "[${BACKUP_DATE}] Running Immich database backup..." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
cd "$IMMICH_DIR"
|
||||||
|
|
||||||
|
# Create backup directory with timestamp
|
||||||
|
mkdir -p "${BACKUP_DIR}/immich-${BACKUP_DATE}"
|
||||||
|
|
||||||
|
# Backup database using Immich's recommended method
|
||||||
|
docker exec -t ${POSTGRES_CONTAINER} pg_dump \
|
||||||
|
--clean \
|
||||||
|
--if-exists \
|
||||||
|
--dbname=${DB_DATABASE_NAME} \
|
||||||
|
--username=${DB_USERNAME} \
|
||||||
|
| gzip > "${BACKUP_DIR}/immich-${BACKUP_DATE}/dump.sql.gz"
|
||||||
|
|
||||||
|
BACKUP_EXIT=${PIPESTATUS[0]}
|
||||||
|
|
||||||
|
if [ $BACKUP_EXIT -ne 0 ]; then
|
||||||
|
echo "[${BACKUP_DATE}] ERROR: Immich database backup failed with exit code ${BACKUP_EXIT}" | tee -a "$LOG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[${BACKUP_DATE}] Immich database backup completed successfully" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Step 2: Backup upload library (photos/videos)
|
||||||
|
echo "[${BACKUP_DATE}] Backing up upload library..." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Get the upload location from docker-compose volumes
|
||||||
|
UPLOAD_LOCATION="/srv/immich/library"
|
||||||
|
|
||||||
|
if [ -d "${UPLOAD_LOCATION}" ]; then
|
||||||
|
tar -czf "${BACKUP_DIR}/immich-${BACKUP_DATE}/library.tar.gz" \
|
||||||
|
-C "$(dirname ${UPLOAD_LOCATION})" \
|
||||||
|
"$(basename ${UPLOAD_LOCATION})" 2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[${BACKUP_DATE}] Upload library backup completed" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "[${BACKUP_DATE}] WARNING: Upload location not found at ${UPLOAD_LOCATION}" | tee -a "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 3: Backup configuration files
|
||||||
|
echo "[${BACKUP_DATE}] Backing up configuration files..." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
cp "${IMMICH_DIR}/docker-compose.yml" "${BACKUP_DIR}/immich-${BACKUP_DATE}/"
|
||||||
|
cp "${IMMICH_DIR}/.env" "${BACKUP_DIR}/immich-${BACKUP_DATE}/"
|
||||||
|
|
||||||
|
echo "[${BACKUP_DATE}] Configuration backup completed" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Step 4: Clean up old backups
|
||||||
|
echo "[${BACKUP_DATE}] Cleaning up backups older than ${KEEP_DAYS} days..." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
find "${BACKUP_DIR}" -maxdepth 1 -type d -name "immich-*" -mtime +${KEEP_DAYS} -exec rm -rf {} \; 2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[${BACKUP_DATE}] Local backup cleanup completed" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Step 5: Create Kopia snapshot of backup directory
|
||||||
|
echo "[${BACKUP_DATE}] Creating Kopia snapshot..." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
kopia snapshot create "${BACKUP_DIR}" \
|
||||||
|
--tags immich,tier1-backup \
|
||||||
|
--description "Immich backup ${BACKUP_DATE}" \
|
||||||
|
2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
KOPIA_EXIT=${PIPESTATUS[0]}
|
||||||
|
|
||||||
|
if [ $KOPIA_EXIT -ne 0 ]; then
|
||||||
|
echo "[${BACKUP_DATE}] WARNING: Kopia snapshot failed with exit code ${KOPIA_EXIT}" | tee -a "$LOG_FILE"
|
||||||
|
echo "[${BACKUP_DATE}] Local Immich backup exists but offsite copy may be incomplete" | tee -a "$LOG_FILE"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[${BACKUP_DATE}] Kopia snapshot completed successfully" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Step 6: Also backup the Immich installation directory (configs, compose files)
|
||||||
|
echo "[${BACKUP_DATE}] Backing up Immich installation directory..." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
kopia snapshot create "${IMMICH_DIR}" \
|
||||||
|
--tags immich,config,docker-compose \
|
||||||
|
--description "Immich config ${BACKUP_DATE}" \
|
||||||
|
2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[${BACKUP_DATE}] Backup process completed successfully" | tee -a "$LOG_FILE"
|
||||||
|
echo "[${BACKUP_DATE}] ========================================" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Optional: Send notification on completion
|
||||||
|
# Add your notification method here (email, webhook, etc.)
|
||||||
```
|
```
|
||||||
|
|
||||||
Find your PostgreSQL container:
|
Make it executable:
|
||||||
```bash
|
```bash
|
||||||
docker ps | grep postgres
|
chmod +x /opt/scripts/backup-immich.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 4: Test Manual Backup
|
Add to crontab (daily at 2 AM):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo /opt/scripts/backup-immich.sh
|
# Edit root's crontab
|
||||||
```
|
crontab -e
|
||||||
|
|
||||||
Verify:
|
# Add this line:
|
||||||
```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
|
0 2 * * * /opt/scripts/backup-immich.sh 2>&1 | logger -t immich-backup
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 6: Configure Kopia Retention
|
### Offsite Backup to Vaults
|
||||||
|
|
||||||
```bash
|
After local Kopia snapshots are created, they sync to your offsite vaults automatically through Kopia's repository configuration.
|
||||||
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
|
## Recovery Procedures
|
||||||
|
|
||||||
|
### Understanding Two Recovery Methods
|
||||||
|
|
||||||
|
We have **two restore methods** depending on the scenario:
|
||||||
|
|
||||||
|
1. **Local Restore** (Preferred): For component-level or same-server recovery
|
||||||
|
2. **Kopia Full Restore**: For complete disaster recovery to a new server
|
||||||
|
|
||||||
### Method 1: Local Restore (Recommended)
|
### Method 1: Local Restore (Recommended)
|
||||||
|
|
||||||
Full system restore:
|
Use this method when:
|
||||||
|
- Restoring on the same/similar server
|
||||||
|
- Restoring specific components (just database, just library, etc.)
|
||||||
|
- Recovering from local Immich backups
|
||||||
|
|
||||||
|
#### Full System Restore
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Stop Immich
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# List available backups
|
||||||
ls -lh /opt/immich-backups/
|
ls -lh /opt/immich-backups/
|
||||||
|
|
||||||
sudo /opt/immich-backups/immich-restore.sh /opt/immich-backups/immich-YYYYMMDD_HHMMSS/
|
# Choose a backup
|
||||||
|
BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
|
||||||
|
|
||||||
|
# Restore database
|
||||||
|
gunzip < ${BACKUP_PATH}/dump.sql.gz | \
|
||||||
|
docker compose exec -T database psql --username=postgres --dbname=immich
|
||||||
|
|
||||||
|
# Restore library
|
||||||
|
tar -xzf ${BACKUP_PATH}/library.tar.gz -C /srv/immich/
|
||||||
|
|
||||||
|
# Restore configuration (review changes first)
|
||||||
|
cp ${BACKUP_PATH}/.env .env.restored
|
||||||
|
cp ${BACKUP_PATH}/docker-compose.yml docker-compose.yml.restored
|
||||||
|
|
||||||
|
# Start Immich
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Monitor logs
|
||||||
|
docker compose logs -f
|
||||||
```
|
```
|
||||||
|
|
||||||
Database-only restore:
|
#### Example: Restore Only Database
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/immich
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Stop Immich
|
||||||
docker compose down
|
docker compose down
|
||||||
docker compose up -d postgres
|
|
||||||
|
# Start only database
|
||||||
|
docker compose up -d database
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
|
# Restore database from backup
|
||||||
BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
|
BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
|
||||||
gunzip < "${BACKUP_PATH}/database.sql.gz" | docker exec -i immich_postgres psql -U postgres
|
gunzip < ${BACKUP_PATH}/dump.sql.gz | \
|
||||||
|
docker compose exec -T database psql --username=postgres --dbname=immich
|
||||||
|
|
||||||
|
# Start all services
|
||||||
docker compose down
|
docker compose down
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
docker compose logs -f
|
||||||
```
|
```
|
||||||
|
|
||||||
Volume-only restore:
|
#### Example: Restore Only Library
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/immich
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Stop Immich
|
||||||
docker compose down
|
docker compose down
|
||||||
|
|
||||||
|
# Restore library from backup
|
||||||
BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
|
BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
|
||||||
VOLUME_NAME="immich_upload"
|
tar -xzf ${BACKUP_PATH}/library.tar.gz -C /srv/immich/
|
||||||
|
|
||||||
docker run --rm \
|
|
||||||
-v "${VOLUME_NAME}:/data" \
|
|
||||||
-v "${BACKUP_PATH}:/backup" \
|
|
||||||
alpine sh -c "cd /data && tar -xzf /backup/${VOLUME_NAME}.tar.gz"
|
|
||||||
|
|
||||||
|
# Start Immich
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### Method 2: Complete Server Rebuild
|
**Note**: For library restore, ensure proper permissions after extraction:
|
||||||
|
```bash
|
||||||
|
chown -R 1000:1000 /srv/immich/library
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method 2: Complete Server Rebuild (Kopia Restore)
|
||||||
|
|
||||||
|
Use this when recovering to a completely new server or when local backups are unavailable.
|
||||||
|
|
||||||
#### Step 1: Prepare New Server
|
#### Step 1: Prepare New Server
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Update system
|
||||||
|
apt update && apt upgrade -y
|
||||||
|
|
||||||
# Install Docker
|
# Install Docker
|
||||||
curl -fsSL https://get.docker.com | sh
|
curl -fsSL https://get.docker.com | sh
|
||||||
sudo systemctl enable docker
|
systemctl enable docker
|
||||||
sudo apt install docker-compose-plugin -y
|
systemctl start docker
|
||||||
|
|
||||||
|
# Install Docker Compose
|
||||||
|
apt install docker-compose-plugin -y
|
||||||
|
|
||||||
# Install Kopia
|
# Install Kopia
|
||||||
curl -s https://kopia.io/signing-key | sudo gpg --dearmor -o /usr/share/keyrings/kopia-keyring.gpg
|
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
|
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
|
apt update
|
||||||
|
apt install kopia -y
|
||||||
|
|
||||||
# Create directories
|
# Create directory structure
|
||||||
sudo mkdir -p /opt/immich /opt/immich-backups /opt/scripts
|
mkdir -p /opt/immich
|
||||||
|
mkdir -p /opt/immich-backups
|
||||||
|
mkdir -p /srv/immich/library
|
||||||
|
mkdir -p /srv/immich/postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Step 2: Connect to Kopia
|
#### Step 2: Restore Kopia Repository
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo kopia repository connect server \
|
# Connect to your offsite vault
|
||||||
--url=https://YOUR_KOPIA_SERVER:51516 \
|
kopia repository connect server \
|
||||||
|
--url=https://192.168.5.10:51516 \
|
||||||
--override-username=admin \
|
--override-username=admin \
|
||||||
--server-cert-fingerprint=YOUR_FINGERPRINT
|
--server-cert-fingerprint=696a4999f594b5273a174fd7cab677d8dd1628f9b9d27e557daa87103ee064b2
|
||||||
|
|
||||||
kopia repository status
|
# List available snapshots
|
||||||
|
kopia snapshot list --tags immich
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Step 3: Restore Configuration
|
#### Step 3: Restore Configuration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Find and restore the config snapshot
|
||||||
kopia snapshot list --tags config
|
kopia snapshot list --tags config
|
||||||
kopia restore <config-snapshot-id> /opt/immich/
|
|
||||||
|
|
||||||
ls -la /opt/immich/docker-compose.yml
|
# Restore to the Immich directory
|
||||||
|
kopia restore <snapshot-id> /opt/immich/
|
||||||
|
|
||||||
|
# Verify critical files
|
||||||
ls -la /opt/immich/.env
|
ls -la /opt/immich/.env
|
||||||
|
ls -la /opt/immich/docker-compose.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Step 4: Restore Backups
|
#### Step 4: Restore Immich Backups Directory
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Restore the entire backup directory from Kopia
|
||||||
kopia snapshot list --tags tier1-backup
|
kopia snapshot list --tags tier1-backup
|
||||||
|
|
||||||
|
# Restore the most recent backup
|
||||||
kopia restore <snapshot-id> /opt/immich-backups/
|
kopia restore <snapshot-id> /opt/immich-backups/
|
||||||
|
|
||||||
|
# Verify backups were restored
|
||||||
ls -la /opt/immich-backups/
|
ls -la /opt/immich-backups/
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Step 5: Run Local Restore
|
#### Step 5: Restore Database and Library
|
||||||
|
|
||||||
```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
|
```bash
|
||||||
cd /opt/immich
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Find the most recent backup
|
||||||
|
LATEST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1)
|
||||||
|
echo "Restoring from: $LATEST_BACKUP"
|
||||||
|
|
||||||
|
# Start database container
|
||||||
|
docker compose up -d database
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Restore database
|
||||||
|
gunzip < ${LATEST_BACKUP}/dump.sql.gz | \
|
||||||
|
docker compose exec -T database psql --username=postgres --dbname=immich
|
||||||
|
|
||||||
|
# Restore library
|
||||||
|
tar -xzf ${LATEST_BACKUP}/library.tar.gz -C /srv/immich/
|
||||||
|
|
||||||
|
# Fix permissions
|
||||||
|
chown -R 1000:1000 /srv/immich/library
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 6: Start and Verify Immich
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Pull latest images (or use versions from backup if preferred)
|
||||||
docker compose pull
|
docker compose pull
|
||||||
|
|
||||||
|
# Start all services
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
|
# Monitor logs
|
||||||
docker compose logs -f
|
docker compose logs -f
|
||||||
```
|
```
|
||||||
|
|
||||||
Verify:
|
#### Step 7: Post-Restore Verification
|
||||||
- [ ] All containers running: `docker compose ps`
|
|
||||||
- [ ] Web interface accessible: `curl -I http://localhost:2283`
|
```bash
|
||||||
- [ ] Database accessible: `docker compose exec postgres psql -U postgres -c "\l"`
|
# Check container status
|
||||||
- [ ] Photos visible: `docker compose exec immich_server ls /usr/src/app/upload/`
|
docker compose ps
|
||||||
- [ ] Login and check albums, timeline, sharing
|
|
||||||
|
# Test web interface
|
||||||
|
curl -I http://localhost:2283
|
||||||
|
|
||||||
|
# Verify database
|
||||||
|
docker compose exec database psql -U postgres -d immich -c "SELECT COUNT(*) FROM users;"
|
||||||
|
|
||||||
|
# Check library storage
|
||||||
|
ls -lah /srv/immich/library/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 2: Restore Individual User's Photos
|
||||||
|
|
||||||
|
To restore a single user's library without affecting others:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Stop Immich temporarily
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# Extract library to temporary location
|
||||||
|
BACKUP_PATH="/opt/immich-backups/immich-YYYYMMDD_HHMMSS"
|
||||||
|
mkdir -p /tmp/immich-restore
|
||||||
|
tar -xzf ${BACKUP_PATH}/library.tar.gz -C /tmp/immich-restore
|
||||||
|
|
||||||
|
# Find the user's directory (using user ID from database)
|
||||||
|
# User libraries are typically in: library/{user-uuid}/
|
||||||
|
USER_UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
|
||||||
|
# Copy user's data back
|
||||||
|
rsync -av /tmp/immich-restore/library/${USER_UUID}/ \
|
||||||
|
/srv/immich/library/${USER_UUID}/
|
||||||
|
|
||||||
|
# Fix permissions
|
||||||
|
chown -R 1000:1000 /srv/immich/library/${USER_UUID}/
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -rf /tmp/immich-restore
|
||||||
|
|
||||||
|
# Start Immich
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Trigger re-scan for this user via the web UI
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 3: Database Recovery Only
|
||||||
|
|
||||||
|
If only the database is corrupted but library data is intact:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Stop Immich
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# Start only database
|
||||||
|
docker compose up -d database
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Restore from most recent backup
|
||||||
|
LATEST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1)
|
||||||
|
gunzip < ${LATEST_BACKUP}/dump.sql.gz | \
|
||||||
|
docker compose exec -T database psql --username=postgres --dbname=immich
|
||||||
|
|
||||||
|
# Start all services
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 4: Configuration Recovery Only
|
||||||
|
|
||||||
|
If you only need to restore configuration files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/immich
|
||||||
|
|
||||||
|
# Find the most recent backup
|
||||||
|
LATEST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1)
|
||||||
|
|
||||||
|
# Stop Immich
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# Backup current config (just in case)
|
||||||
|
cp .env .env.pre-restore
|
||||||
|
cp docker-compose.yml docker-compose.yml.pre-restore
|
||||||
|
|
||||||
|
# Restore config from backup
|
||||||
|
cp ${LATEST_BACKUP}/.env ./
|
||||||
|
cp ${LATEST_BACKUP}/docker-compose.yml ./
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
## Verification and Testing
|
## Verification and Testing
|
||||||
|
|
||||||
Monthly backup verification:
|
### Regular Backup Verification
|
||||||
|
|
||||||
|
Perform monthly restore tests to ensure backups are valid:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Verify local backups
|
# Test restore to temporary location
|
||||||
ls -lth /opt/immich-backups/ | head -5
|
mkdir -p /tmp/backup-test
|
||||||
|
|
||||||
# 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
|
kopia snapshot list --tags immich
|
||||||
|
kopia restore <snapshot-id> /tmp/backup-test/
|
||||||
|
|
||||||
|
# Verify files exist and are readable
|
||||||
|
ls -lah /tmp/backup-test/
|
||||||
|
gunzip < /tmp/backup-test/immich-*/dump.sql.gz | head -100
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -rf /tmp/backup-test/
|
||||||
```
|
```
|
||||||
|
|
||||||
### Backup Monitoring Script
|
### Backup Monitoring Script
|
||||||
|
|
@ -333,157 +629,131 @@ Create `/opt/scripts/check-immich-backup.sh`:
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
LAST_BACKUP=$(ls -td /opt/immich-backups/immich-* | head -1)
|
# Check last backup age
|
||||||
|
LAST_BACKUP=$(ls -td /opt/immich-backups/immich-* 2>/dev/null | head -1)
|
||||||
|
|
||||||
if [ -z "$LAST_BACKUP" ]; then
|
if [ -z "$LAST_BACKUP" ]; then
|
||||||
echo "ERROR: No backups found!"
|
echo "WARNING: No Immich backups found"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BACKUP_DATE=$(basename "$LAST_BACKUP" | sed 's/immich-//')
|
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)
|
BACKUP_EPOCH=$(date -d "${BACKUP_DATE:0:8} ${BACKUP_DATE:9:2}:${BACKUP_DATE:11:2}:${BACKUP_DATE:13:2}" +%s 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$BACKUP_EPOCH" ]; then
|
||||||
|
echo "WARNING: Cannot parse backup date"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
NOW=$(date +%s)
|
NOW=$(date +%s)
|
||||||
AGE_HOURS=$(( ($NOW - $BACKUP_EPOCH) / 3600 ))
|
AGE_HOURS=$(( ($NOW - $BACKUP_EPOCH) / 3600 ))
|
||||||
|
|
||||||
if [ $AGE_HOURS -gt 26 ]; then
|
if [ $AGE_HOURS -gt 26 ]; then
|
||||||
echo "WARNING: Last Immich backup is $AGE_HOURS hours old"
|
echo "WARNING: Last Immich backup is $AGE_HOURS hours old"
|
||||||
|
# Send alert (email, Slack, etc.)
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
echo "OK: Last backup $AGE_HOURS hours ago"
|
echo "OK: Last backup $AGE_HOURS hours ago"
|
||||||
BACKUP_SIZE=$(du -sh "$LAST_BACKUP" | cut -f1)
|
|
||||||
echo "Size: $BACKUP_SIZE"
|
|
||||||
fi
|
fi
|
||||||
```
|
|
||||||
|
|
||||||
Add to cron:
|
# Check Kopia snapshots
|
||||||
```bash
|
KOPIA_LAST=$(kopia snapshot list --tags immich --json 2>/dev/null | jq -r '.[0].startTime' 2>/dev/null)
|
||||||
chmod +x /opt/scripts/check-immich-backup.sh
|
|
||||||
sudo crontab -e
|
if [ -n "$KOPIA_LAST" ]; then
|
||||||
0 8 * * * /opt/scripts/check-immich-backup.sh | logger -t immich-backup-check
|
echo "Last Kopia snapshot: $KOPIA_LAST"
|
||||||
|
else
|
||||||
|
echo "WARNING: Cannot verify Kopia snapshots"
|
||||||
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
## Disaster Recovery Checklist
|
## Disaster Recovery Checklist
|
||||||
|
|
||||||
- [ ] Confirm scope of failure
|
When disaster strikes, follow this checklist:
|
||||||
- [ ] Access offsite Kopia repository
|
|
||||||
- [ ] Provision new server with Docker and Kopia
|
- [ ] Confirm scope of failure (server, storage, specific component)
|
||||||
|
- [ ] Gather server information (hostname, IP, DNS records)
|
||||||
|
- [ ] Access offsite backup vault
|
||||||
|
- [ ] Provision new server (if needed)
|
||||||
|
- [ ] Install Docker and dependencies
|
||||||
- [ ] Connect to Kopia repository
|
- [ ] Connect to Kopia repository
|
||||||
- [ ] Restore configuration from Kopia
|
- [ ] Restore configurations first
|
||||||
- [ ] Restore backup directory from Kopia
|
- [ ] Restore database
|
||||||
- [ ] Run local restore script
|
- [ ] Restore library data
|
||||||
- [ ] Start Immich containers
|
- [ ] Start services and verify
|
||||||
- [ ] Verify web interface and photo access
|
- [ ] Test photo viewing and uploads
|
||||||
- [ ] Update DNS if needed
|
- [ ] Verify user accounts and albums
|
||||||
- [ ] Document issues and lessons learned
|
- [ ] Update DNS records if needed
|
||||||
|
- [ ] Document any issues encountered
|
||||||
|
- [ ] Update recovery procedures based on experience
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
1. **Machine Learning Models**: Can be excluded from backup (re-download automatically)
|
1. **External Mounts**: Your setup has `/export/photos` and `/srv/NextCloud-AIO` mounted as external read-only sources. These are not backed up by this script - ensure they have their own backup strategy.
|
||||||
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
|
2. **Database Password**: The default database password in your .env is `postgres`. Change this to a secure random password for production use.
|
||||||
|
|
||||||
### "Database backup failed"
|
3. **Permissions**: Library files should be owned by UID 1000:1000 for Immich to access them properly:
|
||||||
```bash
|
```bash
|
||||||
docker ps | grep postgres
|
chown -R 1000:1000 /srv/immich/library
|
||||||
docker compose logs postgres
|
```
|
||||||
docker exec immich_postgres pg_dumpall -U postgres | head
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Volume not found"
|
4. **Testing**: Always test recovery procedures in a lab environment before trusting them in production.
|
||||||
```bash
|
|
||||||
docker volume ls | grep immich
|
|
||||||
docker compose up -d --no-start
|
|
||||||
docker volume ls
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Kopia snapshot fails"
|
5. **Documentation**: Keep this guide and server details in a separate location (printed copy, password manager, etc.).
|
||||||
```bash
|
|
||||||
kopia repository status
|
|
||||||
kopia repository connect server --url=...
|
|
||||||
kopia snapshot create /opt/immich-backups
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Photos missing after restore"
|
6. **Retention Policy**: Review Kopia retention settings periodically to balance storage costs with recovery needs.
|
||||||
```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
|
## Backup Architecture Notes
|
||||||
|
|
||||||
### Storage Efficiency Example
|
### Why Two Backup Layers?
|
||||||
|
|
||||||
For a 500GB photo library:
|
**Immich Native Backups** (Tier 1):
|
||||||
|
- ✅ Uses official Immich backup method (`pg_dump`)
|
||||||
|
- ✅ Fast, component-aware backups
|
||||||
|
- ✅ Selective restore (can restore just database or just library)
|
||||||
|
- ✅ Standard PostgreSQL format (portable)
|
||||||
|
- ❌ No deduplication (full copies each time)
|
||||||
|
- ❌ Limited to local storage initially
|
||||||
|
|
||||||
**Without Kopia**:
|
**Kopia Snapshots** (Tier 2):
|
||||||
- 7 days × 500GB = 3.5TB local storage
|
- ✅ Deduplication and compression
|
||||||
|
- ✅ Efficient offsite replication to vaults
|
||||||
|
- ✅ Point-in-time recovery across multiple versions
|
||||||
|
- ✅ Disaster recovery to completely new infrastructure
|
||||||
|
- ❌ Less component-aware (treats as files)
|
||||||
|
- ❌ Slower for granular component restore
|
||||||
|
|
||||||
**With Two-Tier**:
|
### Storage Efficiency
|
||||||
- Local: 3.5TB (7 days)
|
|
||||||
- Kopia vault: ~500GB + (30 × 10GB) = ~800GB
|
|
||||||
- **Savings**: 70-80% in vault storage
|
|
||||||
|
|
||||||
### Component Restore Priority
|
Using this two-tier approach:
|
||||||
|
- **Local**: Immich creates ~7 days of native backups (may be large, but short retention)
|
||||||
|
- **Offsite**: Kopia deduplicates these backups for long-term vault storage (much smaller)
|
||||||
|
|
||||||
| Component | When to Restore | Can Regenerate? | Priority |
|
Example storage calculation (500GB library):
|
||||||
|-----------|----------------|-----------------|----------|
|
- Local: 7 days × 500GB = ~3.5TB (before compression)
|
||||||
| Database | Always | No | Critical |
|
- Kopia (offsite): First backup ~500GB, subsequent backups only store changes (might be <10GB/day after dedup)
|
||||||
| Upload/Library | Always | No | Critical |
|
|
||||||
| Thumbnails | Optional | Yes | Medium |
|
### Compression Formats
|
||||||
| Encoded Videos | Optional | Yes | Low |
|
|
||||||
| Model Cache | Never | Yes | Low |
|
Immich backups use `gzip` compression:
|
||||||
|
- **Database dumps**: `.sql.gz` files (typically 80-90% compression)
|
||||||
|
- **Library archives**: `.tar.gz` files (photos already compressed, ~40-60% reduction)
|
||||||
|
|
||||||
## Additional Resources
|
## Additional Resources
|
||||||
|
|
||||||
- [Immich Official Documentation](https://immich.app/docs)
|
- [Immich Official Backup Documentation](https://immich.app/docs/administration/backup-and-restore)
|
||||||
- [Kopia Documentation](https://kopia.io/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)
|
- [Docker Volume Backup Best Practices](https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes)
|
||||||
|
- [PostgreSQL pg_dump Documentation](https://www.postgresql.org/docs/current/app-pgdump.html)
|
||||||
|
|
||||||
## Revision History
|
## Revision History
|
||||||
|
|
||||||
| Date | Version | Changes |
|
| Date | Version | Changes |
|
||||||
|------|---------|---------|
|
|------|---------|---------|
|
||||||
| 2026-02-13 | 1.0 | Initial documentation - two-tier backup strategy |
|
| 2026-02-13 | 1.0 | Initial documentation - two-tier backup strategy using Immich's native backup method |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: February 13, 2026
|
**Last Updated**: February 13, 2026
|
||||||
|
**Maintained By**: System Administrator
|
||||||
**Review Schedule**: Quarterly
|
**Review Schedule**: Quarterly
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue