VPS Notes
セルフホスト

Immich を 4GB の VPS で動かす: Google フォトから移行して、機械学習と日本語検索まで実測

Google フォト代替の Immich v3 を 4GB の VPS に Docker で立て、Google Takeout から immich-go で移行し、Pixel から自動バックアップするまでを実機で試しました。機械学習なし・あり・多言語モデルのメモリ実測と、日本語で検索できるようにする設定まで。

immichdockergoogle-photosimmich-gotailscalexserver-vps

本記事にはプロモーション (アフィリエイト広告) が含まれます。

「Immich の使い方」「Google フォトから移したい」「VPS で動くのか」と調べている人向けに、先に結論を置きます。

Immich は月 2,480 円の 4GB の VPS でも動きます。Google フォトからの移行は、撮影日時・位置情報・Pixel のモーションフォトまで保ったまま、79 件 (写真 71 枚と動画 8 本) が 6 秒で終わりました。 顔認識や「花火」で探せる検索 (機械学習) も、公式の最小要件 (6GB) を下回る 4GB で動きました。ただし余裕はありません。日本語で検索できる多言語モデルに替えると、スワップを 2GB 中 1.5GB まで使いました。

私の結論は、写真が数百〜数千枚で、機械学習を使うなら 4GB は「動くが、ぎりぎり」、本格的に使うなら 8GB です。以下、その数字を順に見せます。

Immich とは

Google フォトとほぼ同じことを、自分のサーバーで動かすためのオープンソースのソフトです。2022 年に始まり、2025 年 10 月に安定版の v2、2026 年 7 月に v3 が出ました。GitHub のスターは 11.5 万あります (2026-09-23 時点)。

流行っている理由は、私の見立てでは 3 つです。1 つ目は、写真の保管に月額を払い続ける形になったことです。Google フォトは 2021 年 6 月に無料の容量無制限が終わり、iCloud も無料は 5GB です。写真は増える一方なので、払う額も毎年増えます。2 つ目は、自前でも Google フォトと同じ使い心地になったこと。3 つ目は、家族の写真を他社に預けることへの不安です。

逆に言うと、バックアップの責任も自分に来ます。これは最後の「バックアップ」の節で扱います。

この記事で使う環境

XServer VPS クラウド
4GB (4 コア) で月 2,480 円〜。この記事の検証環境です。機械学習まで使うなら 8GB のプランが安心です。
公式サイトを見る

公式の要件と、この記事の結論

公式の要件 は、RAM が最小 6GB・推奨 8GB、CPU が最小 2 コア・推奨 4 コアです。ただし「機械学習を無効にすれば 4GB でも動く」と書かれています。サムネイルと変換した動画で、ライブラリの容量は平均 10〜20% 増えるそうです。

4GB の VPS で実際に測った結果を、先に表にしておきます (常駐エージェントの 700MB 込み)。

状態 VPS 全体の使用 残り スワップ
機械学習なし、待機中 2.1GB 1.7GB 25MB
機械学習なし、動画の変換中 2.6GB - 731MB
機械学習あり (英語モデル)、296 枚の処理中 3.2GB 0.6GB 816MB
機械学習あり (多言語モデル)、296 枚の処理中 3.5GB - 1.48GB
機械学習あり、待機中 2.7〜3.3GB 0.6〜1.1GB 0.8〜1.3GB

1. Docker を入れて、Immich を立てる

Docker は公式のインストーラーで入れます。

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER   # 入り直すと sudo なしで docker が使える

Immich は、公式がリリースごとに docker-compose.yml と .env の雛形を配っています。

mkdir -p ~/immich-app && cd ~/immich-app
V=v3.2.2
curl -fsSL -o docker-compose.yml https://github.com/immich-app/immich/releases/download/$V/docker-compose.yml
curl -fsSL -o .env https://github.com/immich-app/immich/releases/download/$V/example.env

書き換えるのは 2 か所です。

1. ポートを 127.0.0.1 に束縛する。雛形は '2283:2283' で、これだと Docker が ufw を迂回して、インターネット側に 2283 番を開けてしまいます。

sed -i "s/      - .2283:2283./      - 127.0.0.1:2283:2283/" docker-compose.yml

2. .env の DB のパスワードとタイムゾーン。パスワードは雛形のままだと postgres です。

PW=$(openssl rand -hex 24)
sed -i "s/^DB_PASSWORD=postgres$/DB_PASSWORD=$PW/; s/^IMMICH_VERSION=v3$/IMMICH_VERSION=v3.2.2/; s/^# TZ=.*/TZ=Asia\/Tokyo/" .env
chmod 600 .env

IMMICH_VERSION は、雛形の v3 のままだと、docker compose pull のたびに v3 の最新に上がります。私は検証の再現のために v3.2.2 に固定しました。

まずは機械学習なしで起動する

compose には 4 つのサービスがあります。本体 (immich-server)、機械学習 (immich-machine-learning)、DB (database)、キャッシュ (redis) です。機械学習以外の 3 つだけを起動します。

docker compose up -d immich-server redis database

イメージの取得を含めて、90 秒で応答するようになりました。イメージの大きさは、本体 2.5GB、DB 1.0GB です。

コンテナ 起動直後 2 分後
immich_server 1.34GB 868MB
immich_postgres 540MB 369MB
immich_redis 13MB 15MB

Tailscale の中だけに HTTPS で出す

n8n の記事 と同じ方法です。ループバックの 2283 番を、tailnet の中だけに HTTPS つきで出します。インターネット側には出しません。

sudo tailscale serve --bg 2283

これで、Tailscale につないだ端末から https://<VPS の名前>.<tailnet 名>.ts.net で開けます。

2. 初期設定

最初に開くと、管理者の登録画面です。

Immich の管理者登録の画面。メールアドレス、パスワード、名前を入れる

メールアドレスは、ログインの ID に使われるだけです。メールサーバーを設定しない限り、Immich からメールが送られることはありません。私は crz33@immich.local という架空のアドレスにしました。パスワードを忘れても、VPS で docker exec -it immich_server immich-admin reset-admin-password を打てば戻せます。

ログインすると、v3 では初期設定の案内が 7 画面続きます。迷ったのは次の 3 つです。

ストレージテンプレートをオンにしたときのテンプレートとプレビュー。{y}/{y}-{MM}-{dd}/{filename}

ストレージテンプレートは オン をおすすめします。Immich をやめたくなったときや、ディスクごと別の場所に写すときに、Immich なしでも写真が日付のフォルダに並んでいるからです。途中で切り替えると既存のファイルを移し直す処理が走るので、写真を入れる前に決めておくのが楽です。ただし、後で書くように、取り込み直後にちょっとした副作用がありました。

機械学習をオフにする

機械学習のコンテナを起動していないので、設定でもオフにします。オンのままだと、ログに「Machine learning server became unhealthy」が出続けます。

右上のアイコン →「管理」→「設定」→「機械学習設定」の、「機械学習の有効化」をオフにします。

管理画面の機械学習設定で、機械学習の有効化をオフにしたところ

3. Google フォトから移す (Takeout + immich-go)

Takeout で書き出す

Google Takeout で、Google フォトだけを書き出します。「選択をすべて解除」してから Google フォトにチェックを入れ、「すべてのフォト アルバムが含まれます」から書き出すアルバムを選びます。

Takeout の Google フォトのコンテンツ オプション。Photos from 2026 だけを選んだところ

「Photos from 2026」のような項目は、自分で作ったアルバムではなく、Takeout が撮影年ごとに分けたものです。「Archive」はアーカイブした写真、「Trash」はゴミ箱です。私は検証のために 2026 年の分だけにしました。

形式は .zip、サイズは 2GB のまま、「1 回のエクスポート」で作成します。画面には「数時間から数日かかることがある」と出ましたが、1.37GB は 10 分ほど で完了のメールが来ました。ダウンロードのリンクは 1 週間で切れます。

zip の中は、写真 71 枚 (JPEG)、動画 8 本、.MP という拡張子のファイル 24 本、それに写真ごとの JSON が 79 本でした。JSON には撮影日時・位置情報・アルバムなどが入っています。

zip を VPS に送る

Mac から Tailscale 経由で scp しました。1.37GB に 27 分 (毎秒 0.9MB) かかりました。経路は中継ではなく直結でしたが、送っている間は往復の遅延が 150ms まで膨らんでいました。自宅の回線の上りが詰まっていたのだと思います。全部の写真を移すなら、この転送が一番時間のかかる工程になります。

immich-go で取り込む

immich-go は、写真をまとめて Immich に入れるコマンドラインのツールです。Immich の開発元ではなく、個人の開発者が作っているオープンソースです。Takeout の zip を展開せずに読み、写真と JSON を突き合わせて、撮影日時・位置・アルバムを保ったまま入れてくれます。Immich の画面から写真だけを上げると、この JSON は使われません。

入れ方は、Linux 用のバイナリを置くだけです。

curl -fsSL -o /tmp/immich-go.tgz https://github.com/simulot/immich-go/releases/download/v0.32.0/immich-go_Linux_x86_64.tar.gz
tar xzf /tmp/immich-go.tgz -C /tmp immich-go && mv /tmp/immich-go ~/.local/bin/

immich-go が Immich に写真を入れるには、API キー が要ります。プログラムにパスワードを渡す代わりの、いつでも取り消せる専用の鍵です。Immich の画面で、右上のアイコン →「アカウント設定」→「API キー」→「新しい API キー」から作ります。

新しい API キーの作成画面。権限を選ぶ

immich-go はアップロードのほかに、アルバムの作成や撮影日時の書き換えもするので、権限は「全て選択」にしました。移行が終わったら、このキーは消します。

まず --dry-run で、何が入るかを確かめます。

immich-go upload from-google-photos --server=http://127.0.0.1:2283 --api-key=<API キー> --dry-run --no-ui takeout-*.zip
Discovery (Assets):
  discovered image                   :      71  (328.8 MB)
  discovered video                   :       8  (990.7 MB)

Discovery (Non-Assets):
  discovered sidecar                 :      79  (73.3 KB)
  discovered unknown file            :      24  (79.7 MB)

Processing Events:
  associated metadata                :      79

写真と動画の 79 本すべてに、JSON (sidecar) の対応が取れています。気になるのは、.MP の 24 本が「unknown file」として外れていることです。これは後で説明します。問題ありませんでした。

--dry-run を外して本番です。

Total Assets:         79  (1.3 GB)
  Processed:          79  (1.3 GB)
  Errors:              0  (0 B)

同じ VPS の中のアップロードなので、6 秒 で終わりました。そのあと、Immich がサムネイルを作り、動画を変換します。この 2 分ほどの間、本体のコンテナは CPU を 364% (4 コアのほぼすべて) 使い、メモリは 1.73GB まで膨らみ、スワップに 731MB はみ出しました。機械学習なしでも、動画の変換は重い処理です。

ディスクは、元の 1.37GB が、サムネイルと変換後の動画込みで 1.8GB になりました。公式の「10〜20% 増える」より多い 3 割増しで、動画が多いとその分増えるようです。

移行の結果

撮影日は 2026-04-26〜09-19 の範囲で正しく並び、79 枚すべてに位置情報が残って、市区町村まで表示されました。地図には旅行先の写真がそのまま並んでいます。

Immich の地図の画面。石垣島の地図の上に、写真の枚数が丸で表示されている

Pixel のモーションフォトも、動く写真のまま移せました。 さっき外れた .MP は、動画の部分だけを Takeout が別に書き出したものです。同じ名前の .MP.jpg の後ろにも、まったく同じ大きさ (私が調べた 1 枚では 2,426,786 バイト) の動画が埋め込まれていました。Pixel のモーションフォトは、JPEG の後ろに動画をつなげた形式です。Immich はこの JPEG から動画を取り出して、24 枚すべてを動く写真として表示しました。

4. Pixel から自動でバックアップする

Google Play で Immich のアプリを入れ、サーバーの URL に https://<VPS の名前>.<tailnet 名>.ts.net を入れてログインします。スマホにも Tailscale が入っていて、つながっている必要があります。

最初の起動で写真へのアクセスを「許可しない」にすると、バックアップの画面で「アルバムが見つかりません」と出ます。Android の「設定」→「アプリ」→「Immich」→「権限」→「写真と動画」を 「常にすべて許可」 にして、アプリを開き直してください。「選択した写真と動画のみ」にすると、選んだものしかバックアップされません。

アルバムの選択で「Camera」(Pixel で撮った写真) を選びます。

Immich アプリのバックアップするアルバムの選択。Camera の 296 項目を選んでいる

バックアップの画面を開くと、こう出ました。

Immich アプリのバックアップの画面。合計 296、バックアップ済み 79、残り 217

Takeout で入れた 79 枚を、アプリが「バックアップ済み」と数えています。 同じ写真が、Google Takeout と Pixel という別の経路から来ても、二重には入りません。Immich は写真の中身から計算した値で同じものを見分けるからです。残りの 217 件だけが上がりました。

217 件 (5GB ほど) のバックアップには、1 時間ほどかかりました。途中で 20 分ほど止まっていた時間があり、アプリが画面の裏に回っていた間だと思います。アプリには「バッテリーの最適化を無効にすると、バックグラウンドのバックアップの信頼性が上がる」と案内が出ていました。私は最適化をオンのままにしていたので、そのせいで止まった可能性があります。

アップロードしている間は、ブラウザの Immich の動きもカクカクしました。このときサーバーの CPU は 98% が空いていたので、重かったのはサーバーではなく、自宅の回線の上りだと思います。最初のまとめてのバックアップは、使わない時間に流すのが良さそうです。

終わった時点で、タイムラインは 296 件 (写真 248、動画 48) になりました。元のファイルは 6.09GB、サムネイルと変換後の動画込みで 7.5GB (2 割強増し) です。

5. 機械学習を 4GB で動かす

ここからは、公式の最小 (6GB) を下回る領域です。機械学習のコンテナを起動し、設定を戻して有効にします。

docker compose up -d immich-machine-learning

イメージは 1.38GB、起動は 32 秒でした。標準で使われるモデルは 3 つです。

機能 モデル
言葉での検索 (スマート検索) CLIP ViT-B-32__openai
顔検出・顔認識 buffalo_l
写真の中の文字の読み取り (OCR) PP-OCRv5_mobile

管理画面の「待機中のジョブ」から、スマート検索・顔検出・OCR を実行します (私は API から実行しました)。モデルは初回だけ Hugging Face からダウンロードされるので、最初の 1 分ほどは何も進みません。

296 件の処理は 4 分半 (モデルのダウンロード込み) で終わりました。

項目 値
VPS 全体の使用メモリの最大 3.2GB (3.8GB 中)
スワップの最大 816MB
機械学習のコンテナの最大 1.1GB、CPU 318%
強制終了・再起動 なし

顔は 18 人に分けられました。検索は、「sea」「cooking」では狙った写真が上に来ました。ところが「海」では当たりません。 標準の CLIP のモデルは、英語の文章と画像の組み合わせで学習されているからです。

処理が終わっても、機械学習のコンテナは 1GB 近くを持ったままでした。モデルは「300 秒使われなければメモリから外す」設定です。それでも 10 分以上たって 965MB を持っていた理由は、確かめていません。待機中でも VPS 全体で 2.7GB を使い、残りは 1.1GB です。

日本語で検索できるようにする

Immich は、多言語対応の CLIP のモデルに替えられます。公式のドキュメント には、日本語の精度つきで 3 つが載っていました。

モデル メモリ (公式の表) 日本語の精度 (公式の表)
XLM-Roberta-Large-ViT-H-14 4,014MiB 83.95%
nllb-clip-base-siglip__v1 4,675MiB 78.72%
XLM-Roberta-Base-ViT-B-32 3,030MiB 75.93%

4GB で試すなら、一番軽い XLM-Roberta-Base-ViT-B-32 です。管理 →「設定」→「機械学習設定」→「スマート検索」のモデル名を XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k に替えて、スマート検索のジョブを「すべて」でやり直します (私は API で設定を変え、ジョブを実行しました)。

項目 英語のモデル 多言語モデル
296 件の処理時間 (ダウンロード込み) 4 分半 (顔・OCR 込み) 3 分 (検索だけ)
VPS 全体の使用メモリの最大 3.2GB 3.5GB
スワップの最大 816MB 1.48GB (2GB 中)
機械学習のコンテナの最大 1.1GB 1.9GB
強制終了 なし なし

「海」「花火」「料理」が、日本語で当たるようになりました。 ただし、スワップが 2GB のうち 1.5GB まで埋まりました。あと 500MB 足りなければ、どれかのプロセスが強制終了されていたはずです。処理が終わった後も、残りのメモリは 580MB、スワップは 1.3GB 使ったままでした。296 枚でこの状態なので、数千枚を流すと、この張り詰めた状態が長く続きます。

6. バックアップ

Immich には、DB を自動で書き出す機能が最初からオンになっています。設定を見ると、毎日 2:00 に書き出して 14 日分を残す、となっていました。保存先は library/backups です。

注意したいのは、写真も DB のバックアップも、同じ VPS のディスクの中にある ことです。VPS ごと失えば両方消えます。初期設定の案内でも、「3-2-1 バックアップ」(3 つ持つ、2 つは別々の機器、1 つは別の場所) が勧められていました。UPLOAD_LOCATION のフォルダ (この記事では ~/immich-app/library) を丸ごと、手元の PC や別のクラウドに定期的に写してください。

7. 止める・更新する

止めるとき (写真と DB はディスクに残ります):

cd ~/immich-app && docker compose stop

更新するときは、リリースノートで「breaking changes」を確かめてから、.env の IMMICH_VERSION を新しい版に書き換えて取り込み直します。

docker compose pull && docker compose up -d

つまずいた点

Docker を入れてから、移行、スマホのバックアップ、機械学習と多言語モデルの計測まで、全部で 4〜5 時間でした。ほとんどは転送とバックアップの待ち時間 (zip の転送とスマホのバックアップ) で、それを除けば、手を動かしていたのは 30 分ほどです。

次にやること

まとめ

XServer VPS クラウド
この記事の検証環境。機械学習まで常用するなら 8GB、写真が多いならディスクの大きいプランを選んでください。
公式サイトを見る