Googleフォトの代替として注目を集める、オープンソースの写真・動画管理ツールの「Immich」。実際、かなりの完成度で、十分常用出来ます。このImmichを別環境に移行する場合の、バックアップ・復元スクリプトを作成してみました。
スクリプトの内容
まずは最初にこのスクリプトを使った流れや、スクリプトの内容です。
ここで紹介するスクリプトを使った流れ
旧環境(スクリプト1)でバックアップ
chmod +x immich-backup.sh
sudo ./immich-backup.sh
デフォルトでは/opt/lxd-data/immich/backup-YYYYMMDD_HHMMSS/ が作成されます。外付けHDDなどを指定することも可能です。
新環境(スクリプト2)をセットアップ後、復元
chmod +x immich-restore.sh sudo ./immich-restore.sh /opt/lxd-data/immich/backup-YYYYMMDD_HHMMSS
スクリプトのポイント
| 項目 | 内容 |
|---|---|
| DBダンプ | pg_dumpall でロール・スキーマ含む完全ダンプ。Immichが使うvectorスキーマも含まれる |
| 整合性確保 | ダンプ中は immich_server を一時停止し、書き込み競合を防ぐ |
| DB復元 | 新環境のDB認証情報(パスワード)はそのまま維持し、旧データのみ上書き |
| ライブラリ | 既存ライブラリは削除せず .old.日時 にリネームして退避(安全策) |
| 冪等性 | 復元を何度実行しても壊れないよう DROP DATABASE IF EXISTS で対処 |
バックアップスクリプト(immich-backup.sh)
実際に、バックアップスクリプトを作成してバックアップを実行します。LXDコンテナで動かしている場合は、LXDコンテナ内に入って作業します。ちなみに、ここではこの方法でインストールしたImmichを、この方法でインストールした環境に復元しています。
mkdir -p /opt/lxd-data/immich
cd /opt/lxd-data/immich
nano immich-backup.sh
chmod +x immich-backup.sh
#!/bin/bash
set -euo pipefail
# =============================================================
# Immich バックアップスクリプト
#
# 対象環境 (スクリプト1でセットアップした環境):
# - Immich : https://<hostname>.<tailnet>.ts.net (tailscale serve 443)
# - データ : /opt/docker/immich/
#
# 使い方:
# ./immich-backup.sh [バックアップ先ディレクトリ]
#
# 例:
# ./immich-backup.sh # デフォルト: /opt/lxd-data/immich
# ./immich-backup.sh /mnt/usbssd/immich # 外付けSSD等を指定
#
# バックアップ先:
# <指定ディレクトリ>/
# backup-<TIMESTAMP>/
# postgres.dump ← PostgreSQL全体ダンプ (pg_dumpall)
# library.tar.gz ← アップロードファイル一式
# .env ← 環境変数(DBパスワード等)
# BACKUP_INFO ← バックアップメタ情報
#
# 実行場所: Immichが動作しているLXDコンテナ内
# =============================================================
IMMICH_DIR="/opt/docker/immich"
DEFAULT_BACKUP_BASE="/opt/lxd-data/immich"
BACKUP_BASE="${1:-${DEFAULT_BACKUP_BASE}}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_BASE}/backup-${TIMESTAMP}"
# ── カラー出力 ─────────────────────────────────────
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
echo ""
echo "════════════════════════════════════════"
echo " Immich バックアップ"
echo "════════════════════════════════════════"
echo ""
# ── バックアップ先の表示(デフォルトか指定かを明示)
if [ -z "${1:-}" ]; then
echo -e " バックアップ先 : ${BACKUP_DIR}"
echo -e " ${YELLOW}(デフォルト。変更する場合: $0 /mnt/usbssd/immich)${NC}"
else
echo -e " バックアップ先 : ${BACKUP_DIR}"
echo -e " ${GREEN}(引数で指定)${NC}"
fi
echo ""
# ── 前提確認 ──────────────────────────────────────
if [ ! -f "${IMMICH_DIR}/.env" ]; then
echo -e "${RED}ERROR: ${IMMICH_DIR}/.env が見つかりません${NC}"
exit 1
fi
if ! docker ps --format '{{.Names}}' | grep -q "^immich_postgres$"; then
echo -e "${RED}ERROR: immich_postgres コンテナが起動していません${NC}"
echo " docker compose -f ${IMMICH_DIR}/docker-compose.yml up -d を実行してください"
exit 1
fi
# .env から DB情報を読み込む
source "${IMMICH_DIR}/.env"
echo -e " DB名 : ${DB_DATABASE_NAME}"
echo -e " ライブラリ : ${UPLOAD_LOCATION}"
echo ""
# ── バックアップディレクトリ作成 ──────────────────
echo "==> [1/4] バックアップディレクトリを作成..."
mkdir -p "${BACKUP_DIR}"
echo -e " ${GREEN}✓ ${BACKUP_DIR}${NC}"
# ── PostgreSQL ダンプ ─────────────────────────────
echo ""
echo "==> [2/4] PostgreSQLをダンプ中..."
# immich_server を一時停止してDBの整合性を確保
echo -e " ${YELLOW}⚠ immich_server を一時停止します(写真のアップロードが一時的に不可になります)${NC}"
docker stop immich_server immich-machine-learning 2>/dev/null || \
docker stop immich_server 2>/dev/null || true
docker exec immich_postgres pg_dumpall \
-U "${DB_USERNAME}" \
> "${BACKUP_DIR}/postgres.dump"
echo -e " ${GREEN}✓ postgres.dump ($(du -sh "${BACKUP_DIR}/postgres.dump" | cut -f1))${NC}"
# immich_server を再起動
docker start immich_server 2>/dev/null || true
docker start immich-machine-learning 2>/dev/null || true
echo -e " ${GREEN}✓ immich_server を再起動しました${NC}"
# ── ライブラリをアーカイブ ────────────────────────
echo ""
echo "==> [3/4] ライブラリをアーカイブ中..."
echo -e " ${YELLOW}(サイズによっては時間がかかります)${NC}"
tar -czf "${BACKUP_DIR}/library.tar.gz" \
-C "$(dirname "${UPLOAD_LOCATION}")" \
"$(basename "${UPLOAD_LOCATION}")"
echo -e " ${GREEN}✓ library.tar.gz ($(du -sh "${BACKUP_DIR}/library.tar.gz" | cut -f1))${NC}"
# ── .env をコピー ─────────────────────────────────
echo ""
echo "==> [4/4] 設定ファイルをコピー..."
cp "${IMMICH_DIR}/.env" "${BACKUP_DIR}/.env"
chmod 600 "${BACKUP_DIR}/.env"
echo -e " ${GREEN}✓ .env${NC}"
# ── メタ情報を記録 ────────────────────────────────
IMMICH_VERSION_RUNNING=$(docker inspect immich_server \
--format '{{index .Config.Image}}' 2>/dev/null || echo "unknown")
cat > "${BACKUP_DIR}/BACKUP_INFO" <<EOF
BACKUP_TIMESTAMP=${TIMESTAMP}
BACKUP_DATE=$(date '+%Y-%m-%d %H:%M:%S %Z')
SOURCE_DIR=${IMMICH_DIR}
IMMICH_IMAGE=${IMMICH_VERSION_RUNNING}
DB_USERNAME=${DB_USERNAME}
DB_DATABASE_NAME=${DB_DATABASE_NAME}
UPLOAD_LOCATION=${UPLOAD_LOCATION}
POSTGRES_DUMP_SIZE=$(du -sh "${BACKUP_DIR}/postgres.dump" | cut -f1)
LIBRARY_ARCHIVE_SIZE=$(du -sh "${BACKUP_DIR}/library.tar.gz" | cut -f1)
EOF
echo -e " ${GREEN}✓ BACKUP_INFO${NC}"
# ── 完了 ──────────────────────────────────────────
echo ""
echo "════════════════════════════════════════"
echo -e " ${GREEN}✅ バックアップ完了!${NC}"
echo "════════════════════════════════════════"
echo ""
echo " 📁 バックアップ先 : ${BACKUP_DIR}"
echo " 📊 合計サイズ : $(du -sh "${BACKUP_DIR}" | cut -f1)"
echo ""
echo " ファイル一覧:"
ls -lh "${BACKUP_DIR}"
echo ""
echo " 次のステップ:"
echo " 復元先の新環境で immich-restore.sh を実行してください"
echo " バックアップディレクトリ: ${BACKUP_DIR}"
echo ""
echo "════════════════════════════════════════"
echo ""
/opt/lxd-data/immichに保存
デフォルトの場所/opt/lxd-data/immichに保存する場合。
sudo ./immich-backup.sh
外付けHDDなどに保存
外付けHDDなどを指定する場合は、LXDコンテナに追加します。
たとえばホスト接続したデバイスのマウント先が/run/media/user/128だった場合、下記のように指定します(ここではコンテナ内は/opt/ssdにマウントしています。

そして、コンテナに入り、バックアップ先を指定してスクリプトを実行します。
cd /opt/lxd-data/immich
sudo ./immich-backup.sh /opt/ssd
復元スクリプト(immich-restore.sh)
移行先のコンテナでImmichをセットアップします。ここではこの方法でセットアップしています。
セットアップが完了したら、そのコンテナの中で下記を実行してスクリプトを保存します。
mkdir -p /opt/lxd-data/immich
cd /opt/lxd-data/immich
nano immich-restore.sh
chmod +x immich-restore.sh
#!/bin/bash
set -euo pipefail
# =============================================================
# Immich 復元スクリプト
#
# 復元先環境 (スクリプト2でセットアップした環境):
# - Immich : https://<hostname>.<tailnet>.ts.net:3307 (tailscale serve 3307)
# - データ : /opt/docker/immich/
#
# 使い方:
# ./immich-restore.sh /opt/lxd-data/immich/backup-<TIMESTAMP>
#
# 実行タイミング:
# スクリプト2でセットアップ完了後(コンテナ起動済みの状態)に実行
#
# 注意:
# 復元先のDBとライブラリは上書きされます
# =============================================================
IMMICH_DIR="/opt/docker/immich"
BACKUP_DIR="${1:-}"
# ── カラー出力 ─────────────────────────────────────
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
echo ""
echo "════════════════════════════════════════"
echo " Immich 復元"
echo "════════════════════════════════════════"
echo ""
# ── 引数チェック ──────────────────────────────────
if [ -z "${BACKUP_DIR}" ]; then
echo -e "${RED}ERROR: バックアップディレクトリを引数で指定してください${NC}"
echo ""
echo " 使い方:"
echo " $0 /opt/lxd-data/immich/backup-<TIMESTAMP>"
echo ""
echo " 利用可能なバックアップ:"
ls -1d /opt/lxd-data/immich/backup-* 2>/dev/null || echo " (バックアップが見つかりません)"
echo ""
exit 1
fi
if [ ! -d "${BACKUP_DIR}" ]; then
echo -e "${RED}ERROR: バックアップディレクトリが存在しません: ${BACKUP_DIR}${NC}"
exit 1
fi
# ── バックアップファイルの存在確認 ───────────────
for f in postgres.dump library.tar.gz .env; do
if [ ! -f "${BACKUP_DIR}/${f}" ]; then
echo -e "${RED}ERROR: バックアップファイルが見つかりません: ${BACKUP_DIR}/${f}${NC}"
exit 1
fi
done
# ── バックアップ情報を表示 ────────────────────────
if [ -f "${BACKUP_DIR}/BACKUP_INFO" ]; then
echo " 📋 バックアップ情報:"
while IFS='=' read -r key val; do
echo " ${key} = ${val}"
done < "${BACKUP_DIR}/BACKUP_INFO"
echo ""
fi
# ── 復元先の確認 ──────────────────────────────────
if [ ! -f "${IMMICH_DIR}/.env" ]; then
echo -e "${RED}ERROR: ${IMMICH_DIR}/.env が見つかりません${NC}"
echo " 先にスクリプト2 (immich-setup-port3307.sh) を実行してください"
exit 1
fi
if ! docker ps --format '{{.Names}}' | grep -q "^immich_postgres$"; then
echo -e "${RED}ERROR: immich_postgres コンテナが起動していません${NC}"
echo " docker compose -f ${IMMICH_DIR}/docker-compose.yml up -d を実行してください"
exit 1
fi
# 復元先の.envからDB情報を読み込む(新環境のDB認証情報を使う)
source "${IMMICH_DIR}/.env"
echo " 復元元バックアップ : ${BACKUP_DIR}"
echo " 復元先 : ${IMMICH_DIR}"
echo " 復元先DB名 : ${DB_DATABASE_NAME}"
echo " 復元先ライブラリ : ${UPLOAD_LOCATION}"
echo ""
echo -e "${YELLOW}⚠ 警告: 復元先の既存データはすべて上書きされます${NC}"
echo ""
read -rp "続行しますか? [y/N]: " CONFIRM
if [[ ! "${CONFIRM}" =~ ^[Yy]$ ]]; then
echo "キャンセルしました"
exit 0
fi
echo ""
# ── immich_server を停止 ──────────────────────────
echo "==> [1/5] Immichアプリコンテナを停止..."
docker stop immich_server 2>/dev/null || true
docker stop immich-machine-learning 2>/dev/null || true
# machine learningコンテナ名のバリエーションに対応
docker ps --format '{{.Names}}' | grep -E "immich.*(machine|ml)" | xargs -r docker stop || true
echo -e " ${GREEN}✓ 停止完了${NC}"
# ── PostgreSQLを復元 ─────────────────────────────
echo ""
echo "==> [2/5] PostgreSQLを復元中..."
# 既存DBを削除して再作成(冪等な復元のため)
echo " 既存データベースをリセット中..."
docker exec immich_postgres psql \
-U "${DB_USERNAME}" \
-d postgres \
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${DB_DATABASE_NAME}' AND pid <> pg_backend_pid();" \
> /dev/null 2>&1 || true
docker exec immich_postgres psql \
-U "${DB_USERNAME}" \
-d postgres \
-c "DROP DATABASE IF EXISTS ${DB_DATABASE_NAME};" \
> /dev/null 2>&1
docker exec immich_postgres psql \
-U "${DB_USERNAME}" \
-d postgres \
-c "DROP ROLE IF EXISTS ${DB_USERNAME};" \
> /dev/null 2>&1 || true
echo " pg_dumpallデータを復元中(時間がかかる場合があります)..."
docker exec -i immich_postgres psql \
-U "${DB_USERNAME}" \
-d postgres \
< "${BACKUP_DIR}/postgres.dump"
# pg_dumpallでロールごと復元されるため、DBの内部パスワードが旧環境のものに
# 上書きされる。新環境の.envのパスワードに合わせて上書きする。
echo " DBパスワードを新環境の .env に合わせて更新中..."
docker exec immich_postgres psql \
-U "${DB_USERNAME}" \
-d postgres \
-c "ALTER USER ${DB_USERNAME} WITH PASSWORD '${DB_PASSWORD}';" \
> /dev/null 2>&1
echo -e " ${GREEN}✓ PostgreSQL復元完了${NC}"
# ── ライブラリを復元 ─────────────────────────────
echo ""
echo "==> [3/5] ライブラリを復元中..."
echo -e " ${YELLOW}(サイズによっては時間がかかります)${NC}"
# 既存ライブラリを退避(安全のため削除ではなくリネーム)
UPLOAD_PARENT=$(dirname "${UPLOAD_LOCATION}")
UPLOAD_BASENAME=$(basename "${UPLOAD_LOCATION}")
if [ -d "${UPLOAD_LOCATION}" ] && [ "$(ls -A "${UPLOAD_LOCATION}" 2>/dev/null)" ]; then
ARCHIVE_NAME="${UPLOAD_LOCATION}.old.$(date +%Y%m%d_%H%M%S)"
echo -e " ${YELLOW}既存ライブラリを退避: ${ARCHIVE_NAME}${NC}"
mv "${UPLOAD_LOCATION}" "${ARCHIVE_NAME}"
fi
mkdir -p "${UPLOAD_LOCATION}"
tar -xzf "${BACKUP_DIR}/library.tar.gz" \
-C "${UPLOAD_PARENT}"
echo -e " ${GREEN}✓ ライブラリ復元完了${NC}"
# ── .envのDB認証情報を新環境用に維持 ─────────────
# (新環境の.envは既にスクリプト2で生成済みのため上書き不要)
# ただしIMMICH_VERSIONは復元元に合わせる
echo ""
echo "==> [4/5] 設定を確認..."
BACKUP_IMMICH_VERSION=$(grep "^IMMICH_VERSION=" "${BACKUP_DIR}/.env" | cut -d= -f2 || echo "release")
CURRENT_IMMICH_VERSION=$(grep "^IMMICH_VERSION=" "${IMMICH_DIR}/.env" | cut -d= -f2 || echo "release")
if [ "${BACKUP_IMMICH_VERSION}" != "${CURRENT_IMMICH_VERSION}" ]; then
echo -e " ${YELLOW}⚠ IMMICHバージョンが異なります${NC}"
echo " バックアップ元: ${BACKUP_IMMICH_VERSION}"
echo " 復元先 : ${CURRENT_IMMICH_VERSION}"
echo -e " ${YELLOW} 新環境のバージョン (${CURRENT_IMMICH_VERSION}) をそのまま使用します${NC}"
fi
echo -e " ${GREEN}✓ 設定確認完了(新環境の .env を維持)${NC}"
# ── Immichコンテナを再起動 ────────────────────────
echo ""
echo "==> [5/5] Immichを再起動..."
cd "${IMMICH_DIR}"
docker compose up -d
echo " ⏳ Immich serverの起動を待機中(最大180秒)..."
for i in $(seq 1 36); do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:2283/" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" != "000" ]; then
echo -e "\n ${GREEN}✓ Immich server 起動完了 (HTTP ${HTTP_CODE})${NC}"
break
fi
if [ "$i" -eq 36 ]; then
echo -e "\n${RED}ERROR: Immich serverがタイムアウトしました${NC}"
docker logs immich_server --tail=30
exit 1
fi
sleep 5
echo -n "."
done
# ── 完了 ──────────────────────────────────────────
# 新環境のTailscaleドメインを取得
NEW_DOMAIN=$(tailscale status --json 2>/dev/null | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(d.get('Self', {}).get('DNSName', '').rstrip('.'))
" 2>/dev/null || echo "<新環境のホスト名>")
echo ""
echo "════════════════════════════════════════"
echo -e " ${GREEN}✅ 復元完了!${NC}"
echo "════════════════════════════════════════"
echo ""
echo " 🌐 URL : https://${NEW_DOMAIN}:3307"
echo ""
echo " ✅ チェックリスト:"
echo " [ ] ブラウザで https://${NEW_DOMAIN}:3307 にアクセスできるか"
echo " [ ] ログインできるか(メールアドレス・パスワードは移行元と同じ)"
echo " [ ] 写真・アルバムが表示されるか"
echo " [ ] サムネイルが表示されるか(初回は再生成に時間がかかります)"
echo ""
echo " ⚠ 移行元の旧ライブラリが退避されている場合:"
echo " 確認後、不要であれば削除してください:"
echo " ls ${UPLOAD_PARENT}/*.old.* 2>/dev/null"
echo ""
echo "════════════════════════════════════════"
echo ""
復元を実行
バックアップしたファイルを指定して復元します。下記はバックアップファイルを/opt/lxd-data/immichに保存した場合の例。
sudo ./immich-restore.sh backup-YYYYMMDD_HHMMSS
外付けHDDなどから復元
バックアップ時と同様に、ホストのマウントパスをコンテナに追加し、その場所を指定して復元を実行します。
cd /opt/lxd-data/immich
sudo ./immich-restore.sh /opt/ssd/backup-YYYYMMDD_HHMMSS



