概要
Proxmox VE上にローカルLLM環境(Ollama + Open WebUI)を構築し、Cloudflare Tunnelを用いることで、ポート開放不要で外出先からも安全に利用できる環境を実現する手順です。
イメージ図
本環境で実現したいこと
- ローカルLLM環境の構築: Ollama + Open WebUIを採用する。
- 家の外からアクセス可能: Cloudflare Tunnelを用いて実現する。
- ネイティブアプリのような操作感: Open WebUIをPWA化する。
必要なもの
- Proxmox VE 導入済みのマシン [第1回で構築済み]
- NVIDIA製のGPU
- Nginx Proxy Manager(リバースプロキシ) [第2回で構築済み]
- 独自ドメイン
- Cloudflareアカウント
関連記事
自宅サーバ構築記シリーズ:
【自宅サーバ構築記①】自作PCの組み立てとProxmox VE導入
【自宅サーバ構築記②】Proxmox上のLXCでNextcloud AIO構築
システム環境
物理構成(ホスト)
CPU: AMD Ryzen 7 7700 (8C/16T)
GPU: RTX 5060 Ti 16GB × 2枚
メモリ: ADATA DDR5-6000 64GB (32GB × 2)
マザーボード: MSI MAG B850 TOMAHAWK MAX WIFI
ストレージ: Samsung 870 EVO 500GB (SSD) / SANDISK WD_Black SN7100 1TB (NVMeSSD) / WD Red Plus 4TB (HDD)
電源: Corsair RM850e ATX 3.0 (850W)
ケース: Fractal Design Meshify 2 RGB Black TG Light Tint
CPUクーラー: Scythe Mugen 6 Black Edition
仮想環境(ゲスト:KVM)
本サービス用に Proxmox VE 上で割り当てたリソース構成です。
- CPU: 8 Cores
- メモリ: 32GB
- ディスク: 300GB (NVMe SSD)
- GPU: RTX 5060 Ti 16GB × 2枚 (GPUパススルー)
- OS: Ubuntu 24.04.4 Server
目次
- Proxmoxホスト側のGPUパススルー設定
- KVMの作成
- Ubuntu Server 24.04 インストールとディスク拡張
- QEMU Guest Agent のインストール
- NVIDIAドライバ(オープン版)のインストール
- Docker と NVIDIA Container Toolkit の導入
- ローカルLLM環境(Ollama + Open WebUI)の構築
- ポート開放せずに安全に公開する設定(Cloudflare Tunnel と NPM)
- PWA化によるアプリ感覚での運用
1. Proxmoxホスト側のGPUパススルー設定
AMD環境でGPUパススルーを行うためのBIOS設定と、Proxmoxホスト側がGPUを掴まないようにする設定を行います。
1.1. BIOS設定
- Overclocking > EXPO を
Enabledに変更。 - Advanced > Integrated Graphics Configuration > Initiate Graphic Adapter を
PEGからIGDに変更。
(※Proxmoxホストの画面出力は Ryzen 7 7700 の内蔵グラフィックス(iGPU) を使うように設定。) - Overclocking > Advanced CPU Configuration > AMD CBS 内の以下の項目を設定:
-
SVM Enable を
AutoからEnabledに変更(仮想化の有効化) -
IOMMU を
AutoからEnabledに変更(パススルーの有効化) -
PCIe ARI Support を
AutoからEnabledに変更
-
SVM Enable を
「GPUパススルー時の1:1 IOMMUマッピング要求との競合」を避けるため、以下の設定を行います。
- Overclocking > Advanced CPU Configuration > AMD CBS > NBIO Common Options > Kernel DMA Protection Indicator を
AutoからDisabledに変更
「GPUパススルー時の1:1 IOMMUマッピング要求との競合」について
仮想マシン(KVM)にGPUパススルーの設定をした後に起動しようとすると、以下のエラーが表示されました。
failed to setup container for group 12: Failed to set group container: Invalid argument TASK ERROR: start failed: QEMU exited with code 1
また、Proxmoxホスト側の dmesg には以下のエラーが出力されました。
vfio-pci 0000:01:00.0: Firmware has requested this device have a 1:1 IOMMU mapping, rejecting configuring the device without a 1:1 mapping.
ホスト側のDMA保護機能が邪魔をしているようで、BIOS設定にて[Kernel DMA Protection Indicator]を Disabled に変更することで、無事に仮想マシンが起動するようになりました。
1.2. GPUの認識とデバイスIDの確認
次に、Proxmoxホスト側でGPUがどのように認識されているかを確認します。Proxmoxのシェルを開き、以下のコマンドを実行します。
lspci -nn | grep -i nvidia
以下のような出力が得られます(※環境によってPCIアドレスやIDは異なります)。
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GB206 [GeForce RTX 5060 Ti] [10de:2d04] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:22eb] (rev a1)
06:00.0 VGA compatible controller [0300]: NVIDIA Corporation GB206 [GeForce RTX 5060 Ti] [10de:2d04] (rev a1)
06:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:22eb] (rev a1)
ここで表示されている [10de:2d04](VGA本体)と [10de:22eb](オーディオ部分)が、対象GPUの「デバイスID」です。この2つのIDを後の設定ファイルに記述するため、控えておきます。
1.3. 起動時フレームバッファの無効化
Proxmoxホストが起動時にGPUの映像出力を握ってしまわないように、Proxmoxホストのシェルを開き、/etc/default/grub を編集します。
vi /etc/default/grub を実行して、以下の通り書き換えます。
# 編集前
GRUB_CMDLINE_LINUX_DEFAULT="quiet"
# 編集後
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt initcall_blacklist=sysfb_init video=efifb:off"
編集後、update-grub を実行します。
1.4. VFIO関連設定の追加
以下のファイルを作成・編集します。
- VFIOモジュールの自動読み込み
/etc/modules
vfio
vfio_iommu_type1
vfio_pci
- ホスト側GPUドライバの無効化
/etc/modprobe.d/pve-blacklist.conf
blacklist nouveau
blacklist nvidia
blacklist nvidiafb
blacklist nvidia_drm
blacklist nvidia_modeset
- VFIOへのGPUバインド設定
/etc/modprobe.d/vfio.conf
※ここで先ほど調べたデバイスID(VGAとAudioの両方)をカンマ区切りで指定します。
options vfio-pci ids=10de:2d04,10de:22eb disable_vga=1
options vfio_iommu_type1 allow_unsafe_interrupts=1
- KVMのMSR無視設定
/etc/modprobe.d/kvm.conf
options kvm ignore_msrs=1
設定後、update-initramfs -u -k all を実行し、ホストを再起動します。
再起動後、lspci -nnk | grep -A 3 NVIDIA を実行し、対象GPUの Kernel driver in use: が vfio-pci になっていることを確認します。

※GPU1枚につき「VGAコンローラー」と「Audioデバイス」の2ヶ所(今回は2枚挿しのため合計4ヶ所)を確認。
2. KVMの作成
2.1. Ubuntu Server(ISOイメージ)のダウンロード
最新版のライブラリ競合を避けるため、安定した1世代前の 24.04.4 を使用します。Proxmoxの local ストレージ > ISO Images から、公式のURL(https://releases.ubuntu.com/24.04/ubuntu-24.04.4-live-server-amd64.iso)を入力してダウンロードします。
2.2. KVM(仮想マシン)の作成
Proxmox画面右上の「Create VM」から、以下のスペックでVMを作成します。
- OS: ダウンロードした Ubuntu 24.04.4 ISO
-
System: Graphic card:
Default, Machine:q35, BIOS:OVMF(UEFI), Qemu Agent:チェックオン -
Disk: 任意のNVMeストレージ, Size:
300GB, SSD emulation / Discard:チェックオン -
CPU:
8 Cores, Type:host -
Memory:
32768(32GB), Ballooning Device:オフ
2.3. GPUの追加
VM作成後、起動する前に「Hardware」>「Add」>「PCI Device」を選択し、Raw DeviceからパススルーするGPUを2つ追加します。
この時、追加するGPUのオプション設定で 「All Functions」「ROM-Bar」「PCI-Express」 の3つにチェックを入れます。
3. Ubuntu Server 24.04 インストールとディスク拡張
3.1. Ubuntu Server のインストール
VMを起動し、ウィザードに従ってUbuntu Serverをインストールします。
Network configurationの画面では任意の固定IPアドレスを設定します。
[Network configuration](設定例)
Subnet: 192.168.0.0/24
Address: 192.168.0.103
Gateway: 192.168.0.1
Name servers: 192.168.0.1
Search domains: 空欄のまま
3.2. ディスク(LVM)の拡張
Ubuntuのインストーラの仕様上、インストールウィザードの[Storage configuration]画面でサイズを指定せず、そのままポチポチ進めてしまうと、ディスクを300GB割り当てていても初期状態ではルートに約100GBしか割り当てられないため、インストール完了後、SSH等でログインして以下のコマンドで拡張します。
# LVMボリュームを100%拡張
sudo lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv
# ファイルシステムの拡張を適用
sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv
# 結果を確認
df -h
4. QEMU Guest Agent のインストール
ProxmoxからVMのIPアドレス確認や安全にシャットダウンを行うためにエージェントを入れます。
Ubuntu Server上で以下のコマンドを実行します。
# パッケージリストを更新
sudo apt update
# インストール
sudo apt install -y qemu-guest-agent
# 起動
sudo systemctl start qemu-guest-agent
# 起動確認
sudo systemctl status qemu-guest-agent
Proxmoxの管理画面でVMのSummaryにIPアドレスが表示されることを確認します。
5. NVIDIAドライバ(オープン版)のインストール
最初にクローズド版ドライバを試したのですが、nvidia-smiを実行すると「No devices were found」と表示されてGPUを認識できませんでした。
また、dmesgを確認すると「installed in this system requires use of the NVIDIA open kernel modules.」と出力されため、オープン版(NVIDIA Open Kernel Modules)のバージョン580をインストールしていきます。
# まずはシステムを最新化
sudo apt update
sudo apt upgrade -y
# オープン版のドライバをインストール(バージョン580を指定)
sudo apt install -y nvidia-driver-580-open
# 再起動
sudo reboot
再起動後、nvidia-smi コマンドを実行し、GPUが2枚とも正常に認識されていることを確認します。
6. Docker と NVIDIA Container Toolkit の導入
Dockerをインストールして、DockerコンテナからGPUを利用できるようにします。
6.1. Docker Engine のインストール
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 現在のユーザーをDockerグループに追加(sudoなしで実行可能にする)
sudo usermod -aG docker $USER
6.2. NVIDIA Container Toolkit の導入
# リポジトリの追加
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
# ランタイムの再設定
sudo nvidia-ctk runtime configure --runtime=docker
# Docker再起動
sudo systemctl restart docker
7. ローカルLLM環境(Ollama + Open WebUI)の構築
Docker Composeを使って、LLM実行環境の「Ollama」と、チャット画面を提供する「Open WebUI」を構築します。
7.1. docker-compose.yml の作成
以下のコマンドを実行
mkdir ~/ai-env
cd ~/ai-env
vi docker-compose.yml
以下を記述
services:
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ./ollama_data:/root/.ollama
environment:
- OLLAMA_NUM_PARALLEL=1 # 並列処理数
- OLLAMA_KEEP_ALIVE=24h # モデルの保持時間
- OLLAMA_MAX_LOADED_MODELS=1 # モデルの同時ロード数
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all # 2枚のGPUを両方使用
capabilities: [gpu]
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
ports:
- "3000:8080"
environment:
- OLLAMA_BASE_URL=http://ollama:11434
volumes:
- ./webui_data:/app/backend/data
depends_on:
- ollama
7.2. 起動と動作確認
docker compose up -d
起動後、ブラウザから http://<KVMのIP>:3000 にアクセスできれば成功です。
あとは、最初の画面で管理者アカウントを作成し、必要なモデルを取得すれば、チャットを利用できます。
8. ポート開放せずに安全に公開する設定(Cloudflare Tunnel と NPM)
自宅ルータのポート開放を行わずに、家族のスマホなどから家の外からでも安全にアクセスできるようにするため、Cloudflare Zero Trust の Cloudflare Tunnel(トンネル接続) と Cloudflare Access(認証) を利用します。
なお、LAN内ではNginx Proxy Managerを使ってドメイン名をローカル向けに解決できるようにします。
8.1. Tunnel用LXCを作成
Proxmox 管理画面から、cloudflaredをインストールするLXCコンテナを作成します。
右上の「CTを作成」をクリックし、以下の設定でコンテナを作成します。
[General]
Hostname: cloudflared-tunnel
Password: ※任意のパスワード
Unprivileged container: チェック有り(デフォルトのまま)
Nesting: チェック有り(デフォルトのまま)
[Template]
Template: debian-12-standard
[Disks]
Storage: local-lvm
Disk size(GiB): 4
[CPU]
Cores: 1
[Memory]
Memory(MiB): 512(デフォルトのまま)
Swap(MiB): 512(デフォルトのまま)
[Network]
IPv4: Static(デフォルトのまま)
IPv4/CIDR: 192.168.100.204/24 ※任意のIPアドレス
Gateway(IPv4): 192.168.100.1
作成したLXCのコンソールを開き、OSのアップデートをしておきます。
apt update
apt upgrade -y
この後cloudflaredの導入にcurlコマンドを使うのでインストールしておきます。
apt install -y curl
8.2. トンネルの作成とルーティング設定
Cloudflareの管理画面でトンネルを作成し、Tunnel用LXCにcloudflaredを導入します。
※公式ドキュメント:Create a tunnel (dashboard)
その後、Cloudflare Accessでアクセス制御を設定します。
※公式ドキュメント:Publish a self-hosted application to the Internet
-
トンネルの作成:
Cloudflareの管理画面 > ネットワーク > コネクタ > から新しいトンネルを作成します。 -
LXC側へのインストール:
Cloudflare側に表示されたインストールコマンドをLXC上で実行します。 -
ルーティングの設定:
Cloudflareの管理画面 > Zero Trust > ネットワーク > (作成したトンネル名)を開き、「公開アプリケーションルートを追加」します。
サブドメイン:ai
ドメイン: 所有しているドメイン(例:hogehoge.com)
タイプ:HTTP
サービスURL:192.168.0.104:80(Tunnel用LXCのローカルIP)
追加アプリケーション設定 - HTTP設定 - HTTP Hostヘッダー:ai.hogehoge.com -
Cloudflare Access の設定:
Cloudflareの管理画面 > Zero Trust を開き、「はじめる」という趣旨のボタンをクリック。
→チーム名を例: hogehoge.cloudflareaccess.comに設定。
→無料のプラン「$0 / シート / 月」を選択。
Zero Trustの左メニューから Accessコントロール > アプリケーション を開き、「アプリケーションを追加する」 をクリック。
→「セルフホストとプライベート」を選択。
→「新規セルフホストアプリケーションを作成」画面にて、以下を入力し、「作成」をクリック。
パブリックホスト名:ai.hogehoge.com
ポリシールール:メール※利用者のメールアドレスを追加
ポリシー名:Allow Family※任意の名称
アクション:許可(デフォルトのまま)
セッション期間:1month
8.3. Nginx Proxy Manager側の作業
前回の記事で構築済みのNPMにて設定作業を行います。
以下の設定で、Proxy Hosts を追加します。
[Details]タブ
Domain Names: ai.hogehoge.com
Scheme: http
Forward Host IP / Domain: 192.168.0.103 # AI用KVM Ubuntu ServerのローカルIP
Forward Port: 3000
Websockets Support: ON
[SSL]
SSL Certificate:「hogehoge.com, *.hogehoge.com」
Force SSL: OFF
[Settings - Custom Nginx Configuration]:以下を記述
===
# タイムアウト設定
proxy_connect_timeout 60s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# LLMのストリーミング出力(逐次表示)を阻害しないための設定
proxy_buffering off;
proxy_cache off;
===
※Websockets Support:LLMの回答が1文字ずつ表示される「ストリーミング出力」に WebSockets が使用されるのでONにしています。
※タイムアウト設定:AIの回答生成には時間がかかることがあります。Nginxのデフォルト値(60秒)では足りない場合があるため、300秒に設定しています。
※SSL証明書:LAN内でのHTTPS接続を実現するためにSSL設定をしています。(後述のPWA化のため)
8.4. 自宅ルータ(またはローカルDNS)での名前解決
ここまでの設定で、家の外からでもブラウザで 例: https://ai.hogehoge.com/ を開くことで、自宅サーバで動いているローカルLLMにアクセスできるようになりました。
しかし、この時点の構成では、家の中からアクセスする場合でも、一度Cloudflareを経由して自宅サーバに戻ってくる経路になります。
もちろん、KVM(Ubuntu Server)のローカルIPアドレス(http://192.168.0.103:3000/)を直接打てばCloudflareを経由せずLAN内で通信を完結させることは可能です。それでもいいのですが、後述のPWA化にはHTTPSが前提となっているため、PWAを利用したい場合は工夫が必要です。
具体的には、自宅ルータ(またはローカルDNSサーバ)のDNS機能で「ローカルLLMのドメイン名(例:ai.hogehoge.com)」を「NPMのローカルIP(例:192.168.0.102)」に名前解決させ、NPMに導入した証明書を使ってHTTPSを実現します。
【Tips】通信経路とSSL終端について
通信経路によってSSL終端が異なります。
利用するだけなら特に意識することはない部分ですが、豆知識として把握しておくとどこかで役に立つかもしれません。
-
家の外からアクセスする経路:
[ブラウザ] → [Cloudflare] → [Tunnel用LXC] → [NPM] → [Open WebUI]
※SSL通信はCloudflareで一度解除されます。その後、Cloudflareのトンネルを通じて、Tunnel用LXCへ配送されます。 -
家の内からアクセスする経路(自宅ルータで名前解決):
[ブラウザ] → [NPM] → [Open WebUI]
※SSL通信は Nginx Proxy Manager で解除されます。
9. PWA化によるアプリ感覚での運用
Cloudflare Tunnel と NPM によって HTTPS 環境が整ったことで、Open WebUI の PWA(Progressive Web App) が利用可能になります。
これをスマホにインストールすることで、ブラウザを開くことなく、アプリ感覚でOpen WebUIにアクセスできるようになり利便性が向上します。
Android(Chrome)でのPWAインストール手順
- スマホのブラウザ(Chrome)でURL(
例: https://ai.hogehoge.com/)を開きます。 - ブラウザのメニューから 「ホーム画面に追加」 をタップします。
- ホーム画面にアプリアイコンが生成されます。
PWAで起動した画面
→
おわりに
今回はCloudflare Tunnelを導入したことで、家の外から安全にアクセスできるようになりました。もし今後新しいサービスを増やしたい場合も、設定を追加するだけでいいので、拡張性もバッチリです。
ローカルLLMに話しかけると、ファンが唸り、頑張って答えてくれる感じがして愛着が湧いてきます。
さて、ファンが唸ると、それなりに電力を消費しているのだろうと実感できるので、電気代が気になってきました。
今後は、消費電力を可視化できるような仕組み作りを目指したいと思います。
次回以降は、まずはその土台として、Kubernetes(k3s)とGitLab/ArgoCDを組み合わせたGitOps環境を作り、監視システム(Prometheus+Grafana)を立ち上げていきたいと思います!

