ChatGPT PlusやClaude Proの月額20ドルを削減する目的で、Open WebUI(オープンソースのAIチャットフロントエンド)とOllama(ローカルLLM実行基盤)をVPS上にDocker Composeで構築する構成が広がっています。
コスト面では魅力的でも、SREの視点で見ると単一VPS構成には可用性・運用面の落とし穴がいくつも隠れています。個人利用ならまだしも、チームでRAG(検索拡張生成、社内文書を読み込ませて回答させる仕組み)を含めた業務利用に広げる場合は要注意です。
何が起きるか: 単一VPS構成が抱える障害点
典型的な構成では、open-webuiとollamaの2つのコンテナが1台のVPS上で稼働します。Hetzner CloudのCX32(4vCPU・8GBメモリ・80GB NVMe)のようなインスタンス1台に、フロントエンドとモデル推論エンジンの両方を同居させる形です。
この構成で起きる典型的な障害は、大きく3つに分けられます。
- VPSが再起動・停止するとサービス全体が停止する(単一障害点)
- モデルロード時にメモリを使い切りOOM Killer(メモリ不足時にプロセスを強制終了するLinuxカーネル機構)が発動する
- ボリューム(webui-data・ollama-models)のバックアップがなく、障害時にチャット履歴やモデルデータを失う
特に厄介なのがOOM Killerによる予期しないコンテナ停止です。qwen2.5-coder:7bのような7Bパラメータモデルは、量子化していても数GBのメモリを消費します。8GBメモリのVPSで複数モデルを同時にロードしたり、複数ユーザーが同時にリクエストを送ったりすると、あっという間にメモリを使い切ります。
なぜ起きるか: 原因を段階的に分解する
原因は大きく3層に分けて考えると整理しやすいです。
1層目: リソース設計の欠如
docker-compose.ymlの標準的なサンプルには、mem_limitやcpusといったリソース制限が設定されていません。これは開発用途を想定したサンプルだからです。
本番相当の運用にそのまま流用すると、ollamaコンテナがホストのメモリを青天井で消費できてしまいます。1つのコンテナが暴走すると、同居するopen-webuiコンテナやSSHセッションまで巻き込まれて操作不能になるケースがあります。
2層目: ヘルスチェックと再起動戦略の不足
restart: alwaysは指定されていても、これはコンテナが「終了した後」に再起動する設定にすぎません。プロセスが生きたままハングしている状態(メモリスワップで応答が返らない等)は検知できません。
Dockerのhealthcheckディレクティブが設定されていない構成では、Open WebUIがフリーズしていても外形的には「起動中」と表示され続けます。これは監視設計上、見落とされやすいポイントです。
3層目: 可観測性(オブザーバビリティ)の不在
構成例にはログ収集やメトリクス出力の仕組みが含まれていません。CPU使用率・メモリ使用率・レスポンス遅延・トークン生成速度といった指標を継続的に見える化しないと、障害の予兆に気づけません。
たとえば、モデルの応答が徐々に遅くなっている状態は、ディスクI/Oの逼迫やスワップ発生のサインであることが多いですが、docker compose logsだけを眺めていても気づきにくいものです。
自分の構成が該当するか確認する方法
自分の環境がこの落とし穴に該当するかは、以下のコマンドとファイルで確認できます。
# メモリ制限が設定されているか確認
docker inspect ollama --format '{{.HostConfig.Memory}}'
# 0 が返る場合は無制限(要対策)
# 現在のリソース使用状況をリアルタイム確認
docker stats open-webui ollama
# ヘルスチェックが設定されているか確認
docker inspect open-webui --format '{{json .State.Health}}'
# null が返る場合はヘルスチェック未設定docker-compose.ymlを開いて、各サービス定義にdeploy.resources.limitsやmem_limit、healthcheckのキーが存在するかも目視で確認してください。存在しない場合は、対策が未実施の状態です。
また、VPSのプラン自体も確認しておく必要があります。契約しているVPSのメモリサイズと、pull済みモデルのサイズ合計を突き合わせ、余裕があるか把握しておくと安心です。
# pull済みモデルとサイズの一覧
docker exec -it ollama ollama list対策の手順
対策は「リソース制限」「ヘルスチェック」「バックアップ」「監視」の4点です。IaC(Infrastructure as Code、インフラ構成をコードで管理する手法)の考え方に沿って、docker-compose.yml自体に恒久対応として書き込みます。
手順1: メモリ・CPU上限を明示する
services:
ollama:
image: ollama/ollama:latest
mem_limit: 6g
cpus: "3.0"
restart: unless-stoppedVPSの総メモリからOS・open-webui分を差し引いた値をmem_limitに設定します。8GB搭載機なら、ollamaに6GB、open-webuiとOSに残り2GBが目安です。
手順2: ヘルスチェックを追加する
services:
open-webui:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3これにより、プロセスがハングした場合もDockerが異常を検知して自動的にコンテナを再起動できます。
手順3: ボリュームの定期バックアップを設定する
webui-dataとollama-modelsは名前付きボリュームなので、tarコマンドでボリュームの中身を圧縮してバックアップ先へコピーするコマンドをcronに登録し、定期的に別ストレージへ退避します。モデルファイル自体は再pullできますが、チャット履歴やRAG用にアップロードした文書は再現できません。
手順4: 最低限の監視を仕込む
専用の監視基盤を組むほどの規模でなくても、docker statsの出力を定期的にログファイルへ吐き出すcronジョブや、VPSプロバイダが提供する基本的なアラート機能(CPU・メモリ・ディスク使用率のしきい値通知)は有効化しておく価値があります。Hetzner CloudやDigitalOceanは標準のモニタリング機能を持っているため、追加コストなしで有効化できます。
手順5: SLOを自分なりに決めておく
個人・小規模チーム利用であっても、「応答が5秒以上遅延したら異常とみなす」「月間の稼働率目標は99%」のような簡易的なSLO(Service Level Objective、サービスが満たすべき品質目標)を最初に決めておくと、障害対応の判断基準がぶれません。SLOがないと、「今回のダウンは許容範囲か」を毎回感覚で判断することになります。
まとめ
VPS上でOpen WebUIとOllamaを動かす構成は、コスト面では月6〜12ドル程度に抑えられる魅力的な選択肢です。
ただし標準的なdocker-compose.ymlのサンプルは、リソース制限・ヘルスチェック・バックアップ・監視のいずれも欠けた「動くだけ」の構成である点は押さえておく必要があります。
まずdocker inspectでメモリ制限の有無を確認し、未設定ならmem_limitとヘルスチェックをdocker-compose.ymlに追記するところから始めてみてください。あわせてボリュームの定期バックアップとVPSプロバイダの標準モニタリング機能の有効化も、次の一歩として取り組みやすい対策です。