SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor

Immich の画像読み込みエラーを層ごとに切り分ける

Immich の画像が読み込めない、リバースプロキシ越しにタイムアウトする。原因をプロキシ設定、コンテナのログ、サムネイル生成、機械学習ワーカーのメモリ不足の順に切り分け、小さな VPS で何を諦めるかまで決めます。

Immich の画像読み込みエラーで最初に見る場所

Immich の Web UI でサムネイルが灰色のままになる、写真を開くと読み込みエラーが出る、あるいはリバースプロキシ越しにアクセスすると固まってタイムアウトする。原因はほぼ必ず、ブラウザとディスクの間にある層のどれか1つにあります。リバースプロキシのタイムアウト、immich-server の応答、終わっていないサムネイル生成ジョブ、そして機械学習コンテナが止められるほどのメモリ不足です。

やってはいけないのは docker compose restart して様子を見ることです。再起動は待ち行列を空にするので症状は一度消えますが、原因はそのまま残ります。外側の層から順に1つずつ潰していけば、たいてい10分で原因のある層が決まります。

この記事は「どこを見て、何と照合するか」を扱います。「あなたのログにはこう出ているはずだ」とは書きません。ログの文言も数値も環境ごとに違うので、正解はあなたの手元の出力のほうです。

症状はどの層から来ているのか

ブラウザの開発者ツールを開き、ネットワークタブを表示したまま、サムネイルが出ないアルバムを再読み込みします。見るのは失敗したリクエストの URL とステータスコードだけです。

  • 504 Gateway Timeout または 502 Bad Gateway が返る: リクエストはリバースプロキシまで届いています。Immich が時間内に返していないか、返す前に落ちています。次はプロキシのタイムアウトを見ます。
  • 404 が返る: サーバーはリクエストを受け取って「そのファイルはない」と答えています。サムネイルがまだ生成されていないか、生成に失敗しています。次はジョブを見ます。
  • リクエストが pending のまま止まる: プロキシが上流を待っています。読み取りタイムアウトの設定が先です。
  • 画像は出るのに動画だけ止まる: これはトランスコードの話で、この記事の範囲外です。

ステータスコードが分かるだけで、次に開くログが決まります。ここを飛ばすと、当てずっぽうで設定を触ることになります。

リバースプロキシのタイムアウトと本文サイズ

Immich を Nginx や Caddy の裏に置いている場合、ブラウザが見ているタイムアウトは Immich のものではなくプロキシのものです。Nginx の proxy_read_timeout は既定で60秒です。サムネイルがまだない写真を開くと、Immich はその場で元ファイルを読みに行くので、大きな RAW ファイルでは60秒を超えることがあります。超えた瞬間にプロキシが接続を切るので、ブラウザには 504 が返ります。Immich 側は処理を続けているのに、失敗したように見えます。

Immich の公式ドキュメントがリバースプロキシ向けに挙げているディレクティブは次のとおりです(2026年9月時点)。

location / {
    proxy_pass http://127.0.0.1:2283;

    client_max_body_size 50000M;
    client_body_buffer_size 1024k;
    proxy_request_buffering off;

    proxy_http_version 1.1;
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

client_max_body_size が既定のままだと、大きな動画のアップロードが 413 で弾かれます。proxy_request_buffering off は、アップロード本文をいったんプロキシのディスクに溜めてから転送する動作を止めます。これを入れないと、スマートフォンからの一括アップロードで VPS の空き容量が先に尽きます。

sudo nginx -t
sudo systemctl reload nginx

nginx -t が syntax is ok と test is successful の2行を出すまで reload しないでください。設定ファイルが壊れたまま reload すると、Immich どころか同じ Nginx の裏にある全部が落ちます。

これでタイムアウトが消えたなら、問題はプロキシではなく「Immich が遅い」ことです。600秒待てば返るというのは解決ではなく、次の層に進む合図です。各ディレクティブが何をしているのかを一行ずつ確認したいなら、Nginx のリバースプロキシ設定を一行ずつ読み解く記事 を先に読んでください。

どのコンテナのログを読むのか

ログを開く前に、自分のスタックに何が動いているかを確認します。

docker compose ps

現在の公式 compose ファイルのサービス名は immich-server、immich-machine-learning、redis、database です。コンテナ名はそれぞれ immich_server、immich_machine_learning、immich_redis、immich_postgres になります。

古い構成では immich_microservices という5つ目のコンテナが並んでいます。サムネイル生成や機械学習の呼び出しといったバックグラウンドジョブは、以前はこのコンテナが担当していて、その後サーバー側に統合されました。どちらの構成かで読むべきログが変わるので、記憶ではなく docker compose ps の出力で判断してください。

docker compose logs -f --tail=200 immich-server
docker compose logs -f --tail=200 immich-machine-learning

読む順番は症状で決まります。画像の配信そのものが失敗しているなら immich-server。サムネイルやスマート検索のジョブが進まないなら、ジョブを回しているコンテナ(統合済みなら immich-server、分かれているなら immich_microservices)。顔検出やスマート検索だけが失敗するなら immich-machine-learning です。

直近の行だけを眺めても判断できません。時刻を付けて出力し、ブラウザで失敗した時刻と突き合わせてください。

docker compose logs --since 10m --timestamps immich-server

サムネイルが生成されていない場合

404 が返っていたなら、そのファイルのサムネイルが存在しません。管理画面の Administration > Jobs を開くと、ジョブごとの待ち件数と実行中件数が見えます。Thumbnail Generation の待ち件数が減らないなら、ジョブが止まっているか、失敗して再投入され続けています。

最初に疑うのはディスクです。サムネイルはファイルとして書かれるので、保存先が満杯なら生成は必ず失敗します。

df -h
docker compose exec immich-server df -h /usr/src/app/upload

ホスト側とコンテナ内の両方を見るのが大事です。UPLOAD_LOCATION が別のディスクにマウントされているなら、ホストの / に空きがあっても意味がありません。

docker compose logs --tail=500 immich-server | grep -i -E "job|thumbnail|error"

出てきた行がそのまま原因です。権限の拒否が出ているなら、ホスト側ディレクトリの所有者とコンテナ内で動いているユーザーの UID がずれています。この UID と GID の合わせ方は Docker コンテナの PUID と PGID の考え方 にまとめてあります。

原因を直したあと、Jobs 画面から対象のジョブだけ流し直します。All ではなく Missing を選ぶと、すでに成功しているぶんを作り直さずに済みます。ライブラリが大きいほどこの差は大きくなります。

機械学習コンテナが止められているケース

メモリ2GB から4GB の VPS で最も多いのがこれです。機械学習コンテナは、スマート検索用の CLIP モデルと顔検出モデルをメモリに読み込みます。読み込んだ瞬間が使用量のピークです。空きが足りなければ、カーネルかコンテナのメモリ上限がそのプロセスを止めます。ブラウザから見ると、検索が返らない、顔のグループが増えない、アップロード直後のサムネイルが出ない、という別々の症状に見えます。

まず実際の使用量を測ります。

docker stats --no-stream

MEM USAGE / LIMIT と MEM % の列を、何もしていないときと、Jobs 画面から Smart Search を流しているときの両方で見てください。アイドル時の数字だけでは何も分かりません。

コンテナが止められたかどうかは、推測しなくても確認できます。

docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}} {{.State.Status}}' immich_machine_learning

OOMKilled が true なら、そのコンテナはメモリ上限に当たって止められています。compose ファイルで deploy.resources.limits.memory を設定していると、ホストに空きがあってもその上限で止まります。上限の書き方と、上限を付けるべきかどうかは Docker Compose でコンテナのメモリ上限を決める記事 にあります。

ホスト全体のメモリ不足なら、カーネル側にも記録が残ります。

sudo dmesg -T | grep -i -E "out of memory|killed process"

Immich の公式 FAQ は、SIGKILL や終了コード 137 に触れているログはメモリ不足である可能性が高い、と書いています(2026年9月時点)。フォーラムでよく報告される worker was sent code 139 のような行についても、まずは「何かがワーカーを終了させた」という症状として扱ってください。数字の意味を調べるより、同じ時刻の docker stats と dmesg の出力を突き合わせるほうが早く、確実です。

診断のあとに決めること

ここまでで原因の層が決まっているはずです。メモリ不足だった場合、選べる手はいくつかあり、どれも何かと引き換えです。

ジョブの同時実行数を下げる。 Administration > Settings > Job Settings で、ジョブごとの同時実行数を変えられます。Immich の FAQ は、普通のマシンなら2か3の同時実行で CPU を使い切る、それ以上に増やすのはサーバーを過負荷にするだけだ、という趣旨を書いています。小さい VPS では Smart Search と Face Detection を1にしてください。処理は遅くなります。ただし止められて再投入され、また止められるループよりは確実に速く終わります。

機械学習の負荷そのものを絞る。 機械学習コンテナ側の環境変数で、メモリと CPU の使い方を変えられます。

immich-machine-learning:
  environment:
    MACHINE_LEARNING_WORKERS: 1
    MACHINE_LEARNING_MODEL_TTL: 60
    MACHINE_LEARNING_REQUEST_THREADS: 2

MACHINE_LEARNING_WORKERS はワーカープロセス数で、既定は1です。MACHINE_LEARNING_MODEL_TTL はモデルがメモリに残る秒数で、既定は300です。短くするとアイドル時にモデルが解放されるので常駐メモリが減りますが、次のジョブでの読み込みが増えます。MACHINE_LEARNING_REQUEST_THREADS の既定は CPU コア数です。顔検出モデルを buffalo_l から buffalo_s に変えるのも、管理画面から選べる軽量化です。変更したら docker compose up -d でコンテナを作り直します。restart では環境変数は反映されません。

機械学習を止める。 Administration > Settings > Machine Learning Settings で、全体、またはモデル種別ごとに無効にできます。失うものははっきりしています。スマート検索(言葉で写真を探す機能)と顔のグループ化、そして Explore ページが機能しなくなります。公式ドキュメントも、無効にすると検索と Explore の体験が悪くなると明記しています。日付とアルバムで探すなら実用上の支障はありません。写真の内容で探すつもりなら、この選択肢は諦めることを意味します。決めるのはあなたです。

swap を足す。 足りないのが数百 MB なら、swap で越えられることがあります。

sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
free -h
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

free -h の Swap 行に容量が出れば有効です。/etc/fstab への追記を忘れると次の再起動で消えて、同じ症状に戻ります。swap はディスクなので、モデルの読み込みは目に見えて遅くなります。これは「落ちない」ための対処であって「速くする」ための対処ではありません。仮想化方式によっては swap の作成が許可されていないので、swapon がエラーを返したらこの手は使えません。

機械学習を別のマシンに逃がす。 家に常時起動の PC があるなら、機械学習コンテナだけそちらで動かせます。Immich が公式にサポートしている構成です。

name: immich_remote_ml
services:
  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    restart: always
    ports:
      - 3003:3003

volumes:
  model-cache:

VPS 側では管理画面の Machine Learning Settings で Add URL を押し、http://ip:port の形式で家のマシンの URL を追加します。既定の http://immich-machine-learning:3003 を消さずに追加しておくと、家のマシンが落ちているときに VPS 側の処理へ戻ります。ただし写真そのものがこの経路を流れるので、3003番ポートをインターネットに公開しないでください。VPN の内側からだけ到達できるようにします。

VPS を増強する。 上の手がどれも何かを諦めるものであるのに対して、これは素直な解決です。自分のライブラリの規模に対して何 GB 必要なのかは、枚数と使うモデルで変わります。Immich に必要な RAM とストレージの見積もり を先に読んで、いまの VPS が単純に足りていないのか、設定が悪いだけなのかを判断してから課金してください。

この記事で扱っていないこと

動画の再生が始まらない、途中で止まるという症状はトランスコードの設定の話で、別の記事になります。家の外から Immich に接続する方法も別の話です。どちらも「画像が読み込めない」の原因ではないので、この記事の手順で追いかけないでください。まだ構築したばかりで初期同期そのものが終わっていない可能性があるなら、Google フォトの代わりに Immich を自分で立てる手順 の最後の確認ステップに戻るのが早道です。

切り分けの順番

  1. ブラウザの開発者ツールでステータスコードを確認する。504 か 404 かで進む先が変わる。
  2. リバースプロキシの proxy_read_timeout と client_max_body_size を確認し、nginx -t を通してから reload する。
  3. docker compose ps で、自分のスタックのコンテナ構成を確認する。
  4. 失敗した時刻に合わせて immich-server と immich-machine-learning のログを読む。
  5. docker stats と docker inspect でメモリ使用量と OOMKilled を確認する。
  6. そのうえで、同時実行数を下げるか、機械学習を止めるか、swap を足すか、外部に逃がすか、増強するかを決める。

この順番の価値は、各ステップが次のステップを1つに絞ることにあります。504 を見た人がジョブ設定をいじる理由はありませんし、OOMKilled が false の人が swap を足しても何も変わりません。

FAQ

Immich のサムネイルだけが灰色のままなのはなぜですか

ブラウザの開発者ツールでそのサムネイルのリクエストが 404 を返しているなら、ファイルがまだ生成されていません。Administration > Jobs で Thumbnail Generation の待ち件数を見てください。減らないなら、保存先の空き容量を docker compose exec immich-server df -h /usr/src/app/upload で確認し、docker compose logs --tail=500 immich-server からジョブのエラー行を拾います。ディスク満杯と、ホスト側ディレクトリの所有者がコンテナ内ユーザーと合っていない権限エラーが、よく見つかる2つです。

504 Gateway Timeout はリバースプロキシと Immich のどちらの問題ですか

どちらでもあります。504 は「プロキシが上流を待ちきれずに切った」という意味なので、まずプロキシ側の proxy_read_timeout を延ばして症状が消えるか確かめます。消えたなら、プロキシの設定は直りましたが、Immich が遅い理由は残っています。消えないなら、Immich がそもそも応答を返していないので、docker compose logs --since 10m --timestamps immich-server を失敗時刻と突き合わせて読んでください。

機械学習を無効にすると何ができなくなりますか

Administration > Settings > Machine Learning Settings から、全体またはモデル種別ごとに無効にできます。失うのは、言葉で写真を探すスマート検索、顔のグループ化、そして Explore ページです。公式ドキュメントも、無効にすると検索と Explore の体験が悪くなると書いています。日付とアルバムで写真を探す使い方なら、無効のままでも困りません。あとから有効に戻して、対象のジョブを流し直すこともできます。

2GB の VPS で Immich は動きますか

閲覧とアップロードだけなら動く構成ですが、機械学習ジョブを既定の設定のまま有効にしておくと、ワーカーが止められるという報告が多い領域です。断定せずに測ってください。Smart Search ジョブを流しながら docker stats --no-stream を見て、そのあと docker inspect --format '{{.State.OOMKilled}}' immich_machine_learning が true を返すかどうかで判断できます。true なら、同時実行数を1にするか、機械学習を切るか、増強するかの選択になります。

ログにある worker was sent code 139 はどう読めばいいですか

その数字を説明しようとしないでください。読み取るべきなのは「何かがワーカープロセスを終了させた」という事実だけです。同じ時刻の sudo dmesg -T | grep -i -E "out of memory|killed process" と docker inspect の OOMKilled を見れば、メモリ不足かどうかが分かります。Immich の公式 FAQ は、SIGKILL や終了コード 137 を含むログについてメモリ不足の可能性が高いとしています(2026年9月時点)。あなたの環境で出ているコードが何であれ、確認する場所は同じです。