Open WebUIをVPSにセルフホスト|ClaudeやOpenAIのAPIを自分の画面で使う

PRこの記事にはアフィリエイト広告が含まれます。紹介したサービスに申し込まれると、運営者が報酬を受け取ることがあります。評価や順位は報酬で変えていません。広告表記について

Open WebUI は、ChatGPT のようなチャット画面を自分のサーバーで動かせるツールです。Claude や OpenAI などの API キーを登録すると、その画面から各社のモデルを使えます。API の利用料は使った分だけなので、使う量が少なければ、VPS の料金と API の利用料を合わせても、月額制のサービスより安く済むことがあります。

この記事では、Open WebUI を VPS に Docker で構築し、HTTPS で公開するまでを解説します。手元で動かして分かった、最初の登録の仕様と、外から誰でも開ける状態にしないための設定も紹介します。

必要なメモリ

Open WebUI の公式ドキュメントには、必要なメモリやCPUの最小要件が書かれていません。そこで、手元で測りました。言語モデルは API で外部を使う前提のため、Open WebUI と Caddy だけの値です。

Open WebUI v0.12.3(標準イメージ)と Caddy のメモリ使用量(アイドル時)合計 943 MiB。半分以上の余裕が残るのは 2GB のプランからです。OS や Docker 自体が使う分は含みません。
Open WebUI: 930 MiBリバースプロキシ(Caddy): 13 MiB1GB 余裕8%2GB 余裕54%合計 943 MiB
  • Open WebUI930 MiB
  • リバースプロキシ(Caddy)13 MiB
コンテナ別の内訳を表で見る
グループコンテナメモリ
Open WebUIopen-webui930 MiB
リバースプロキシ(Caddy)caddy13 MiB

測定日 。macOS 上の Docker(OrbStack、arm64、割り当て 16GB)。docker stats で5回(60秒間隔)取得した中央値。標準のイメージ(:v0.12.3)を Caddy の後ろで起動し、管理者を登録して1回チャットしたあと、10分以上待って測定。言語モデル(検証では Ollama)は別のコンテナのため含まない。

Open WebUI v0.12.3(slim イメージ)と Caddy のメモリ使用量(アイドル時)合計 303 MiB。半分以上の余裕が残るのは 1GB のプランからです。OS や Docker 自体が使う分は含みません。
Open WebUI: 292 MiBリバースプロキシ(Caddy): 12 MiB1GB 余裕70%合計 303 MiB
  • Open WebUI292 MiB
  • リバースプロキシ(Caddy)12 MiB
コンテナ別の内訳を表で見る
グループコンテナメモリ
Open WebUIopen-webui292 MiB
リバースプロキシ(Caddy)caddy12 MiB

測定日 。macOS 上の Docker(OrbStack、arm64、割り当て 16GB)。docker stats で5回(60秒間隔)取得した中央値。slim イメージ(:v0.12.3-slim)を Caddy の後ろで起動し、標準イメージで作ったデータのまま切り替え、10分以上待って測定。言語モデル(検証では Ollama)は別のコンテナのため含まない。

  • 標準のイメージは、文書検索(RAG)や音声のための機械学習の仕組みを含んでいて、何もしていなくても約0.9GiB を使いました。2GB のプランを選んでください。
  • slim イメージは、こうした機能を外したものです。手元では約0.3GiB で、1GB のプランでも収まります。ただし、文書の取り込みや音声などの機能には、別のサービスが必要になります。チャットだけに使うなら、slim イメージで十分です。使うときは、image: を ghcr.io/open-webui/open-webui:v0.12.3-slim にします。

構成

インターネット ──> Caddy(80/443、HTTPS) ──> Open WebUI(外には公開しない)──> Claude・OpenAI などの API

公式の手順は -p 3000:8080 で Open WebUI のポートを直接公開しますが、基本手順の記事で書いたとおり、Docker で公開したポートは ufw の設定を通りません。この記事では、ほかの記事と同じく、Caddy だけを外に出します。

手順1:VPS に Docker を入れ、DNS を設定する

Docker Compose で HTTPS 公開する基本手順のとおり、Docker を公式の手順で入れます。使うドメイン(例:chat.example.com)の A レコードを、VPS の IP アドレスに向けておきます。

手順2:ファイルを作る

作業用のディレクトリ(例:~/open-webui)に、3つのファイルを作ります。

compose.yaml:

services:
  caddy:
    image: caddy:2.11.7
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      - WEBUI_DOMAIN=${WEBUI_DOMAIN}
      - ALLOWED_IPS=${ALLOWED_IPS}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

  open-webui:
    image: ghcr.io/open-webui/open-webui:v0.12.3
    restart: unless-stopped
    expose:
      - "8080"
    environment:
      - WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}
      - WEBUI_URL=https://${WEBUI_DOMAIN}
      - WEBUI_SESSION_COOKIE_SECURE=true
      - CORS_ALLOW_ORIGIN=https://${WEBUI_DOMAIN}
    volumes:
      - open-webui:/app/backend/data

volumes:
  caddy_data:
  caddy_config:
  open-webui:

Caddyfile:

{$WEBUI_DOMAIN} {
	# 許可した IP アドレス以外からのアクセスを断る(ALLOWED_IPS は空白区切りで複数書ける)
	@blocked not remote_ip {$ALLOWED_IPS}
	respond @blocked "Forbidden" 403
	reverse_proxy open-webui:8080
}

.env:

WEBUI_DOMAIN=chat.example.com
# 自宅や職場の IP アドレス。分からなければ「IP アドレス 確認」で検索して調べる
ALLOWED_IPS=203.0.113.10/32
# openssl rand -hex 32 で作った値に置き換える
WEBUI_SECRET_KEY=ここに値を入れる

ポイントは次のとおりです。

  • イメージは版の番号で固定します。 公式の例にある :main は、新しい変更が入るたびに中身が変わるタグです。公式のセキュリティの手引きも、本番では版の番号を指定し、自動では更新しないよう勧めています。
  • WEBUI_SECRET_KEY は必ず固定値にします。 ログイン用のトークンの署名と、一部の保存データ(MCP の認証情報やアプリの API キーなど)の暗号化に使われます。設定しないとコンテナの中で自動で作られ、コンテナを作り直したときに変わることがあります。そうなると、全員がログアウトされ、保存した情報が読めなくなる場合があると、公式ドキュメントは説明しています。
  • WEBUI_URL、WEBUI_SESSION_COOKIE_SECURE、CORS_ALLOW_ORIGIN は、自分のドメインと HTTPS で使う前提の設定です。CORS_ALLOW_ORIGIN は既定ではすべての送信元を許すため、公式のセキュリティの手引きに従って自分のドメインに絞っています。WEBUI_URL は初回の起動後は画面の設定が優先されるため、あとからドメインを変えるときは管理者パネルで変えます。
  • ALLOWED_IPS で、開ける人を自分に限ります。 理由は後の「外から誰でも開ける状態にしない」で説明します。

手順3:起動して、すぐに管理者を登録する

docker compose up -d

初回の起動では、標準のイメージは文書検索用のモデルを自動でダウンロードします。手元では起動が終わるまで約2分かかりました。その間、docker compose ps では unhealthy と表示されることがありますが、待てば正常になります。

起動したら、すぐに https://chat.example.com を開いて、管理者のアカウントを作ります。最初に登録した人が管理者になります。 手元で確かめたところ、1人目の登録で管理者になり、そのあとの2人目の登録は拒否されました。公式ドキュメントによると、最初の登録のあと、新規の登録は自動で閉じます。

手順4:Claude や OpenAI の API をつなぐ

API キーは、画面から登録します。管理者でログインし、管理者パネル → 設定 → 接続 の「OpenAI API接続の管理」で接続を追加し、接続先の URL と API キーを入れます。保存しただけでは接続を確かめないため、接続の確認(Verify Connection)も行います。

使いたいモデル 接続先の URL API キーの発行元
OpenAI(GPT) https://api.openai.com/v1 OpenAI のダッシュボード
Claude https://api.anthropic.com/v1 Anthropic Console

Claude は、Anthropic が用意している OpenAI 互換の API で接続します(Open WebUI 公式の手順)。ただし、Anthropic 自身がこの互換の API を「評価やテスト向け」としていることを、Open WebUI の公式ドキュメントが注意として紹介しています。PDF の処理やプロンプトのキャッシュなどを使うには、互換の API ではなく Anthropic のネイティブの API が勧められています。

環境変数で接続先を設定するときの注意

接続先は OPENAI_API_BASE_URL などの環境変数でも指定できます。ただし、Open WebUI の多くの設定は、初回の起動時だけ環境変数から読み込まれ、そのあとはデータベースに保存された値が使われます。あとから環境変数を書き換えても反映されないことがあるため、起動後の変更は画面から行うのが確実です。

外から誰でも開ける状態にしない

Open WebUI の公式のセキュリティの手引きは、認証などの仕組みを前に置かずに、インターネットに直接公開しないよう明記しています。

GitHub で公表されたセキュリティアドバイザリを数えると、2026年1月1日から10月24日までに159件ありました(Critical 1件、High 66件)。多くは「ログインできる人」が前提の問題ですが、管理者のログイン画面が誰からも見える状態は避けたいところです。

この記事の構成では、Caddy で許可した IP アドレス以外からのアクセスを断るようにしました。手元で、許可していない IP アドレスからは 403(拒否)が返り、許可した IP アドレスからは開けることを確かめています。

  • 外出先など、IP アドレスが変わる場所から使いたい場合は、VPN(Tailscale など)を使い、VPN の IP アドレスだけを許可する方法があります。
  • 家族やチームで使う場合は、管理者パネルからユーザーを追加するか、IP アドレスの制限をかけたまま、必要な間だけ新規登録を有効にします。公式の既定では、新しく登録した人は「承認待ち」になり、管理者が承認するまで使えません。公式のセキュリティの手引きは、信頼できないネットワークから届く状態で登録を開けたままにしないよう求めています。

バックアップ

チャットの履歴、ユーザー、設定は、すべて open-webui ボリュームに入っています。公式の例とほぼ同じ方法で、ボリュームを固めます(自動でダウンロードされるファイルは除きます)。

docker compose stop open-webui
docker run --rm -v open-webui_open-webui:/data -v "$PWD":/backup alpine:3.22 \
  tar czf /backup/openwebui-$(date +%Y%m%d).tar.gz --exclude=./cache -C /data .
docker compose start open-webui

ボリューム名は「ディレクトリ名_open-webui」になります。docker volume ls で確認してください。

--exclude=./cache は、自動でダウンロードされる文書検索用のモデルなどを除くための指定です。手元では、この cache だけで約1GB あり、除くとバックアップは約30KB になりました(チャットが少ない状態)。文書検索用のモデルは、復元後の起動時に再びダウンロードされるはずです。

復元は、次のように行います。公式ドキュメントの復元コマンドは、ファイルの中の形が違うため、このバックアップにはそのまま使えません。

docker compose stop open-webui
docker run --rm -v open-webui_open-webui:/data -v "$PWD":/backup alpine:3.22 \
  sh -c "rm -rf /data/* && tar xzf /backup/openwebui-20261024.tar.gz -C /data"
docker compose start open-webui

ファイル名の日付は、自分のバックアップに合わせてください。手元では、slim イメージの環境でこの手順を実行し、既存の管理者でログインできることを確かめました。公式ドキュメントは、データベースの変換は一方通行で、コンテナの版を戻しても元に戻らないと説明しています。更新の前には必ずバックアップを取ってください。

更新

compose.yaml の image: の版を書き換えて、起動し直します。

docker compose pull open-webui
docker compose up -d

新しい版は GitHub のリリース一覧で確認できます。2026年10月は数日おきに新しい版が出ていました。

VPS の選び方

標準のイメージなら 2GB、slim イメージなら 1GB のプランで足ります。言語モデルは外部の API で動かすため、CPU の性能はあまり要りません。

メモリ 2GB のプランの比較(税込み、1ヶ月契約の安い順)
サービスとプラン1ヶ月契約最長契約の月額ファイアウォール新規申込
KAGOYA CLOUD VPS2GB、2コア、NVMe SSD 200GB770円715円12ヶ月(年額)初期状態は全許可セキュリティグループ停止中
WebARENA Indigo2GB、2コア、SSD 40GB814円814円なし(時間課金のみ)未確認セキュリティグループ停止中
シンVPS大容量メモリ 2GB、2コア、NVMe SSD 50GB1,040円950円36ヶ月未確認パケットフィルター受付中
ConoHa VPS2GB、3コア、SSD 100GB1,259円初回 1,007円903円36ヶ月初回 677円初期状態で拒否セキュリティグループ受付中
ABLENET VPSL1、2コア、SSD 40GB1,476円初回 1,234円1,130円12ヶ月(年払い)初回 968円記載なし受付中
さくらのVPS2G、3コア、SSD 100GB1,738円1,594円12ヶ月一部だけ制限パケットフィルター一部のみ受付
XServer VPS2GB、3コア、NVMe SSD 50GB受付停止の告知(7月14日)のあと、再開の告知が見当たらない1,980円1,265円36ヶ月初期状態で拒否パケットフィルター受付中

に各社の公式ページで確認した価格です。1ヶ月契約は更新時の価格で、時間課金・日額課金しかない会社は月額の上限を載せています(ConoHa VPS は一括前払いの1ヶ月の価格で、時間課金はこれより上限が高くなります)。最長契約の月額は、最も長い契約期間の料金を1ヶ月あたりに換算した額です。ファイアウォールは、VPS の管理画面で設定できるものの初期状態です。申込の受付状況は変わりやすいため、契約の前に公式ページで確かめてください。

  • ConoHa VPS: 初回の価格は 2026年10月29日までのキャンペーン価格で、申し込み時だけ適用されます
  • ABLENET VPS: 初回の価格は「契約時特別価格」で、最初の支払いだけに適用されます(期限の記載なし)

Open WebUI の上で Ollama などのモデルを VPS 内で動かす場合は、話が変わります。モデルの大きさに応じて、さらに多くのメモリと CPU が必要です。

出典

  1. Quick Start(Open WebUI 公式ドキュメント)(に確認)
  2. Starting with Anthropic(Open WebUI 公式ドキュメント)(に確認)
  3. Environment Variable Configuration(Open WebUI 公式ドキュメント)(に確認)
  4. Hardening(Open WebUI 公式ドキュメント)(に確認)
  5. Updating(Open WebUI 公式ドキュメント)(に確認)
  6. Open WebUI のセキュリティアドバイザリ(GitHub)(に確認)