前回は、TrueNAS SCALE Community Apps版Immichを更新した直後に、serverコンテナが再起動を繰り返した経緯をまとめました。
表示されていたのは、no pg_hba.conf entryとdatabase "immich" does not exist。いったんApp 1.11.8/Immich v2.4.1/PostgreSQL 15へ戻すことで写真へ再アクセスできましたが、これは一時復旧です。
今回は、その正常なPostgreSQL 15を出発点にして、どのようにPostgreSQL 18への移行をやり直したのかを、実際の作業順で振り返ります。
この記事の目的は、環境差を無視して貼り付ける「完全復旧コマンド集」ではありません。どこで止まり、何を残し、なぜ壊れたPG18を修理せず作り直したのか。その判断と確認方法を記録することです。
今回の出発点とゴール
作業再開時と、最終的な状態は次のとおりです。
| 項目 | 作業再開時 | 最終状態 |
|---|---|---|
| TrueNAS SCALE | 25.10.5 | 25.10.5 |
| Immich | v2.4.1 | v3.0.3 |
| TrueNAS App | 1.11.8 | 1.14.30 |
| PostgreSQL | 15 | 18 |
| 状態 | 一時復旧・写真表示OK | 正常稼働 |
| DB確認 | asset 24,501件 |
asset 24,501件 |
今回のゴールは、アプリ一覧を緑色にすることではありません。
- 移行元PG15と移行先PG18で
asset件数が一致する - Immichの
serverコンテナが再起動ループへ戻らない - Web UIへログインできる
- 複数のサムネイルと写真を表示できる
- 検索結果が返る
ここまで確認できた状態を復旧完了としました。
最初に「戻せる状態」が残っているか確認した
自動移行に失敗した直後は、早くPG18を起動したくなります。しかし、最初に確認したのは移行先ではなく、移行前へ戻せる材料でした。
postgres_dataには、PostgreSQL更新前のスナップショットが残っていました。

スナップショットの詳細画面では、対象データセット、作成時刻、使用量、参照量を確認できます。この画面で「スナップショットがある」ことと、「正しいデータセットのスナップショットである」ことを分けて確認しました。
ロールバック画面では停止条件まで確認
TrueNASのスナップショットロールバックは、現在のデータセットを過去の状態へ置き換える操作です。新しいスナップショットや子データセット、クローンの扱いによって結果が変わります。

画面を開いて対象と安全確認オプションを確認しました。ロールバックできるからといって、無条件に実行してよいわけではありません。TrueNAS公式ドキュメントでも、ロールバックは現在のデータを破棄し得る操作として警告されています。
今回、最終的なPG18移行に使った中心データは、スナップショットから直接復元した物理ファイルではなく、正常稼働へ戻したPG15から取得した論理バックアップです。スナップショットは、作業前の状態を残す保険として扱いました。
PG15を「正本」として固定した
一時復旧後のPG15では、Web UIと写真表示が正常でした。そこで、このPG15を移行元の正本として扱い、失敗したPG18側へ追加の修正を重ねるのをやめました。
まずImmichの書き込みを止め、PG15側で次の情報を記録しました。
SELECT current_setting('server_version');
SELECT current_database();
SELECT count(*) FROM asset;
ここで確認できたasset件数は24,501件です。この数値を、PG18復元後の照合基準にしました。
確保した2種類のバックアップ
作業時点では、次の2種類を残していました。
- 自動移行処理が作成したPG15の移行前物理アーカイブ:約294MB
- 正常稼働へ戻したPG15から取得した論理バックアップ:約152MB
物理アーカイブは最後の保険として保持し、PG18への復元には論理バックアップを使いました。PostgreSQLのメジャーバージョンをまたぐため、PG15のデータディレクトリをPG18へそのままコピーする方法は採用していません。
論理バックアップは、pg_dumpの終了コード、gzipの整合性、ファイルが空でないことを確認してから次へ進みました。
pg_dump --clean --if-exists --dbname=immich --username=immich \
| gzip -1 > "$DUMP"
gzip -t "$DUMP"
test -s "$DUMP"
sha256sum "$DUMP"
ここで重要だったのは、「ファイルができた」だけで成功扱いしなかったことです。パイプ途中のpg_dumpが失敗しても、空に近いgzipファイルだけが残る可能性があるため、終了コードと中身を確認しました。
失敗したPG18は削除せず隔離した
自動移行で作られたPG18には、接続拒否とImmichデータベース不在が同時に出ていました。
pg_hba.confの確認や編集も試しましたが、状況は改善しませんでした。接続許可を直しても、移行先にimmichデータベースが正しく復元されていなければ、写真管理サービスは起動できません。
そこで、既存PG18をその場で直す方針を中止しました。ただし、原因調査と切り戻しに使えるよう、削除はしていません。PG18ディレクトリを日時付きの名前へ変更して隔離し、元のPG15、論理バックアップ、移行前アーカイブを残したまま、新しいPG18を作る方針へ切り替えました。
この判断により、次の試行が失敗してもPG15へ戻れる状態を維持できました。
空のPostgreSQL 18を新しく作成
新しいPG18には、Immich向けのPostgreSQL 18/VectorChordイメージを使いました。
起動後、すぐにバックアップを流し込まず、まずPG18単体で次を確認しました。
pg_isreadyが成功する- PostgreSQLのバージョンが18である
immichデータベースが存在する- TCP接続で
SELECT 1;が成功する
psql -h 127.0.0.1 -U immich -d immich -Atc 'SELECT 1;'
この確認で、新しく作ったPG18ではHBAを手編集しなくても接続できることが分かりました。つまり、元の失敗環境へ設定を足し続けるより、正常な初期状態を作り直したほうが問題を切り分けやすい状況でした。
PG15の論理バックアップをPG18へ復元
PG18単体の起動を確認してから、PG15の論理バックアップを復元しました。
復元時に重視したのは、途中エラーを見逃さないことです。ON_ERROR_STOPを有効にし、単一トランザクションで処理しました。
gunzip --stdout "$DUMP" \
| docker exec -i immich-pg18-recovery \
psql \
--dbname=immich \
--username=immich \
--single-transaction \
--set ON_ERROR_STOP=on
これにより、エラーが出たのに後続処理だけ進み、不完全なデータベースを正常と誤認する危険を減らしました。
復元後は、PG18側で再びasset件数を取得しました。
SELECT count(*) FROM asset;
結果は24,501件。移行元PG15と一致しました。さらに、PG18からスキーマだけを再度ダンプできることも確認してから、TrueNAS Appへ引き渡しました。
DBサイズはPostgreSQLのメジャーバージョンやインデックス再構築で変わるため、完全一致の条件にはしていません。今回は、件数、スキーマ読出し、アプリの実動作を組み合わせて判断しました。
Immich v3.0.3をPG18へ接続
一時コンテナを停止・削除しても、PG18のデータはデータセット上に残ります。そのPG18をTrueNAS Community Apps版Immichへ引き渡し、Immich v3.0.3/App 1.14.30を起動しました。
最初はserverコンテナがStartingになりました。

前回はこのあとExitedへ戻って再起動を繰り返したため、ここではまだ成功扱いにしていません。コンテナ状態とログを確認し、次のエラーが再発しないか見守りました。
no pg_hba.conf entry
database "immich" does not exist
その後、ImmichはRunningへ移行しました。

Running表示のあとに行った最終確認
アプリ一覧が緑になっただけでは、DB移行の成功は証明できません。そこで、次の順に確認しました。
- PostgreSQLのバージョンが18であること
- データベース名が
immichであること - PG18の
asset件数が24,501件で、PG15の記録と一致すること serverコンテナが再起動ループへ戻らないこと- Web UIへログインできること
- タイムラインで複数のサムネイルが表示されること
- 複数の写真を個別に開けること
- 検索を実行し、結果が返ること
この確認を終えて、ようやく「Immich v3.0.3+PostgreSQL 18への移行完了」と判断しました。
実際に問題を解決したのは何だったか
今回、最終的に効いたのはpg_hba.confの追記でも、失敗したPG18への上書き修理でもありませんでした。
正常に動くPG15を正本として固定し、論理バックアップを取得。失敗したPG18を残したまま、新しいPG18を初期化し、エラー時に停止する条件で復元したことです。
正常なPG15
↓ 論理バックアップ
検証済みdump
↓ 新規クラスタへ復元
新しいPG18
↓ 件数・スキーマ確認
Immich v3.0.3
この流れにしたことで、移行元を壊さず、各段階で「次へ進んでよいか」を判断できました。
今回の移行で特に重要だった判断
エラーを1行だけで判断しなかった
no pg_hba.conf entryは強い手掛かりですが、同時にdatabase "immich" does not existも出ていました。接続設定とデータ移行失敗を分けて考えたことで、HBA編集だけに固執せずに済みました。
ロールバックを最終解決にしなかった
PG15への復帰は、正常な写真サービスと正しいDBを取り戻すための足場でした。その足場から論理バックアップを取り、新しいPG18へ進んだことで、最終状態をImmich v3.0.3にできました。
壊れた移行先を削除しなかった
失敗したPG18、正常なPG15、物理アーカイブ、論理バックアップを残したまま作業しました。復旧作業中の安心感だけでなく、想定外のエラーが出たときに戻れる選択肢を維持できました。
成功判定を複数にした
コンテナ状態、DBバージョン、asset件数、スキーマ読出し、Web UI、写真表示、検索を組み合わせました。どれか1つだけでは見逃す不整合を、段階的な確認で減らせます。
まとめ
今回のPostgreSQL 15→18移行は、最初の自動更新では失敗しました。
しかし、PG15へ一時的に戻したことで、正常なDBを正本として確保できました。そこから論理バックアップを取り、失敗したPG18を隔離し、新しいPG18へ復元。移行元と移行先で24,501件のassetレコードを照合し、Immich v3.0.3のWeb UI、複数写真、サムネイル、検索まで確認できました。
復旧作業で一番大切だったのは、急いでエラーを消すことではありません。「戻れる状態を残す」「確認できた事実だけで次へ進む」「成功条件をアプリの色だけにしない」という3点でした。


コメント