Article
Proxmox VE で 1 台の自宅サーバに Linux Desktop VM を構築し、RTX 3060 Ti を GPU パススルーするまで
Intel NUC 系マシンに Proxmox VE を導入し、iGPU をホスト用、RTX 3060 Ti を Ubuntu Desktop VM 用として GPU パススルーしました。IOMMU 確認、VFIO 設定、PVE の VM 設定、トラブルシュート、実ゲーム検証までをまとめます。
はじめに
自宅サーバ兼ホームラボ用途で、スペックに余裕のある 1 台の物理マシンを Proxmox VE、以下 PVE、で仮想化基盤化しました。
目的は、物理マシンを増やさずに、Linux デスクトップ環境、たまに使う Windows 環境、Samba サーバ環境などを分離して運用することです。特に重要だったのは、Linux Desktop VM に NVIDIA GPU をパススルーし、物理マシンとほぼ同じ操作感で使えるかどうかでした。
最終的には、PVE 上の Ubuntu Desktop VM に RTX 3060 Ti をパススルーし、実ディスプレイ出力、HDMI 音声、USB ゲームパッド、キーボード操作、Xbox Cloud Gaming の Fortnite まで、ネイティブマシンと違和感ないレベルで動作確認できました。
検証環境
今回確認した環境は以下です。
| 項目 | 内容 |
|---|---|
| 用途 | 自宅サーバ、ホームラボ、Linux Desktop VM、Samba、Windows VM |
| 物理マシン | Intel NUC9i7QN 系 |
| CPU | 12 コア相当の構成 |
| メモリ | 32GB |
| ホスト OS | Proxmox VE |
| ホスト表示用 GPU | Intel UHD Graphics 630 |
| パススルー対象 GPU | NVIDIA GA104 / GeForce RTX 3060 Ti Lite Hash Rate |
| GPU PCI ID | `10de:2489` |
| GPU Audio PCI ID | `10de:228b` |
| 内蔵ストレージ | SSD 500GiB、PVE インストール先 |
| 外付けストレージ | USB 3.2 SSD 1TiB、USB SSD 500GiB x2 |
| 既存データ領域 | Samba 公開用 HDD 6TiB、既存データ維持前提 |構成上の重要な方針は、iGPU を PVE ホスト用、RTX 3060 Ti を Linux Desktop VM 用に分けることです。これにより、PVE ホストは管理基盤に徹し、重い GUI やゲーム用途は VM 側に逃がせます。
最終構成
現時点で確定した構成は以下です。
| 役割 | 構成 |
|---|---|
| PVE ホスト | iGPU 使用、Web UI / SSH 管理、基盤専用 |
| Ubuntu Desktop VM | RTX 3060 Ti パススルー、HDMI 音声、USB 入力、ゲーム用途 |
| Windows VM | 仮想 GPU + RDP 前提、稀に必要な Windows アプリ用 |
| Samba VM | 今後構築予定。6TiB HDD をできれば VM にディスク単位で渡す方針 |
| LXC | 小物サービス用。Docker Hub イメージとは別物として扱う |ストレージは以下の方針にしました。
| PVE Storage | 用途 |
|---|---|
| `local` | `Container template` のみ |
| `local-lvm` | `Disk image`, `Container`。VM 本体や LXC 実体用 |
| `usb-1tb` | `Disk image`, `ISO image`。外付け USB 3.2 SSD 1TiB |local は 100GB 程度で容量が少ないため、ISO のような嵩張るものは usb-1tb に寄せる判断にしました。
事前検証: Ubuntu ホスト上で IOMMU と GPU パススルー可能性を確認
PVE を本格導入する前に、もともと入っていた Ubuntu Desktop 上で KVM/libvirt を使い、GPU パススルーの成立性を検証しました。
まず IOMMU / VT-d が有効かを確認しました。
sudo dmesg | grep -Ei 'DMAR|IOMMU'重要だったログは以下です。
DMAR-IR: Enabled IRQ remapping in x2apic mode
iommu: Default domain type: Translated
pci 0000:01:00.0: Adding to iommu group 1
pci 0000:01:00.1: Adding to iommu group 1
DMAR: Intel(R) Virtualization Technology for Directed I/OVT-d は Virtualization Technology for Directed I/O の略です。このログから、Intel VT-d / IOMMU が OS から認識され、IOMMU グループも作成されていると判断しました。
GPU とオーディオ function は次のように見えていました。
lspci -nn | grep -E 'VGA|3D|Audio'00:02.0 VGA compatible controller [0300]: Intel Corporation CoffeeLake-H GT2 [UHD Graphics 630] [8086:3e9b]
00:1f.3 Audio device [0403]: Intel Corporation Cannon Lake PCH cAVS [8086:a348] (rev 10)
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104 [GeForce RTX 3060 Ti Lite Hash Rate] [10de:2489] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation GA104 High Definition Audio Controller [10de:228b] (rev a1)IOMMU グループの中身は以下でした。
for d in /sys/kernel/iommu_groups/1/devices/*; do
lspci -nns "${d##*/}"
done00:01.0 PCI bridge [0604]: Intel Corporation 6th-10th Gen Core Processor PCIe Controller (x16) [8086:1901] (rev 07)
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104 [GeForce RTX 3060 Ti Lite Hash Rate] [10de:2489] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation GA104 High Definition Audio Controller [10de:228b] (rev a1)00:01.0 の PCI bridge と、GPU 本体、GPU Audio が同じグループです。理想は GPU と Audio のみのグループですが、この程度であれば十分素直な構成だと判断しました。無関係な SATA、NIC、USB コントローラが混ざっていなかったためです。
Ubuntu ホスト上で VFIO bind を検証
最初は RTX 3060 Ti がホスト側の NVIDIA ドライバに掴まれていました。
lspci -nnk -s 01:00.0
lspci -nnk -s 01:00.101:00.0 ...
Kernel driver in use: nvidia
Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
01:00.1 ...
Kernel driver in use: snd_hda_intel
Kernel modules: snd_hda_intelそこで一時的にホストドライバから外し、vfio-pci に bind しました。
sudo modprobe vfio
sudo modprobe vfio_pci
sudo modprobe vfio_iommu_type1
echo 0000:01:00.1 | sudo tee /sys/bus/pci/devices/0000:01:00.1/driver/unbind
echo 0000:01:00.0 | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver/unbind
echo 10de 228b | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id
echo 10de 2489 | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id最初に GPU 本体を unbind しようとした際、以下のログが出ました。
NVRM: Attempting to remove device 0000:01:00.0 with non-zero usage count!これは NVIDIA ドライバがまだ GPU を使用中であることを意味します。対処として、SSH 経由で入った上で display manager や NVIDIA 関連サービスを停止し、NVIDIA 関連モジュールを外してから再実行しました。
最終的に以下のように vfio-pci に bind できました。
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104 [GeForce RTX 3060 Ti Lite Hash Rate] [10de:2489] (rev a1)
Kernel driver in use: vfio-pci
Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
01:00.1 Audio device [0403]: NVIDIA Corporation GA104 High Definition Audio Controller [10de:228b] (rev a1)
Kernel driver in use: vfio-pci
Kernel modules: snd_hda_intelUbuntu ホスト上の KVM/libvirt でゲスト VM に GPU を渡す
次に、Ubuntu ホスト上の KVM/libvirt VM に GPU を渡しました。ゲスト内で NVIDIA GA104 と HDMI Audio が見えることを確認し、NVIDIA ドライバを入れました。
当初は nvidia-driver-580-open を指定して入れましたが、nvidia-smi 実行時に以下のようなメッセージが出ました。
Make sure that the latest NVIDIA driver is installed and running.その後、個別指定をやめて以下を実行しました。
sudo ubuntu-drivers install
sudo reboot再起動後、nvidia-smi が成功しました。

さらに、NVIDIA 側の物理端子にディスプレイを接続したところ、ゲスト VM の画面が遅延なく表示されました。HDMI 音声もディスプレイ側スピーカーから出力され、USB Host Device として Logitech F310 Gamepad を渡したところ、VM 側で認識されました。
最終的には、Google Chrome 上の Xbox Cloud Gaming で Fortnite をプレイし、映像、音声、ゲームパッド入力ともに物理マシンと違和感ない操作感であることを確認しました。
この時点で、ハードウェアとしては GPU パススルーに十分向いていると判断しました。
PVE インストールとストレージ設計
その後、物理マシンに PVE をインストールしました。インストール先は内蔵 SSD 500GiB です。
PVE インストール直後、ストレージは自動的に以下のように分かれていました。
local: 約 100GBlocal-lvm: 約 360GB
最初は再インストールして 1 つにまとめることも考えましたが、PVE の標準構成として自然なため、そのまま使うことにしました。
追加で USB 3.2 接続の外付け SSD 1TiB を XFS で初期化し、UUID 指定で /etc/fstab に登録しました。USB 外付けなので、デバイス名ではなく UUID で指定し、nofail を付ける方針です。
UUID=<uuid> /mnt/ext1tb xfs defaults,nofail 0 2PVE の Web UI から Datacenter → Storage → Add → Directory で、usb-1tb という名前の Directory storage として登録しました。
最終的な content type は以下です。
local: Container template
local-lvm: Disk image, Container
usb-1tb: Disk image, ISO imageContainer template は LXC コンテナの元になるテンプレートで、Docker image とは別物です。Container は PVE 管理下の LXC コンテナ実体です。PVE が Docker Hub の image を直接 LXC として扱うわけではないため、Docker Hub image を使う場合は、基本的に VM の中で Docker を動かす方針が自然です。
PVE ホスト側の IOMMU 確認
PVE 上でも IOMMU を確認しました。
dmesg | grep -Ei 'DMAR|IOMMU'PVE 上では Ubuntu ホスト時代と少しログが変わりました。特に iGPU 00:02.0 が IOMMU group に入り、以下のような DMAR fault が出ていました。
DMAR: [DMA Read NO_PASID] Request device [00:02.0] fault addr 0x0 [fault reason 0x06] PTE Read access is not set00:02.0 は Intel UHD 630、つまり PVE ホスト表示用の iGPU です。今回の本命は dGPU の RTX 3060 Ti なので、これは dGPU パススルー自体の失敗を意味するものではないと判断しました。
実害が出る場合は intel_iommu=igfx_off によって iGPU だけ IOMMU 対象から外す選択肢もあります。ただし、この時点では PVE ホストの Web UI / SSH と dGPU パススルー検証を優先し、まずは進めました。
PVE ホストで dGPU を vfio-pci に取らせる
PVE インストール直後、dGPU はまだ vfio-pci ではなく nouveau と snd_hda_intel に掴まれていました。
lspci -nnk -s 01:00.0
lspci -nnk -s 01:00.101:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104 [GeForce RTX 3060 Ti Lite Hash Rate] [10de:2489] (rev a1)
Kernel driver in use: nouveau
Kernel modules: nvidiafb, nouveau
01:00.1 Audio device [0403]: NVIDIA Corporation GA104 High Definition Audio Controller [10de:228b] (rev a1)
Kernel driver in use: snd_hda_intel
Kernel modules: snd_hda_intelそこで、起動時から vfio-pci に取らせる設定を入れました。
/etc/modules は obsolete と表示されたため、/etc/modules-load.d/ を使いました。
cat >/etc/modules-load.d/vfio.conf <<'EOF'
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
EOFvfio-pci に対象 PCI ID を指定します。
cat >/etc/modprobe.d/vfio.conf <<'EOF'
options vfio-pci ids=10de:2489,10de:228b
EOFnouveau なども blacklist しました。
cat >/etc/modprobe.d/blacklist-gpu.conf <<'EOF'
blacklist nouveau
blacklist nvidiafb
EOFGRUB のカーネル引数は以下の方針です。
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"必要に応じて iGPU の DMAR fault 対策として以下も候補になります。
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on,igfx_off iommu=pt"設定反映後、initramfs を更新して再起動しました。
update-grub
update-initramfs -u -k all
reboot再起動後、以下のように dGPU と Audio の両方が vfio-pci に取られました。
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104 [GeForce RTX 3060 Ti Lite Hash Rate] [10de:2489] (rev a1)
Kernel driver in use: vfio-pci
Kernel modules: nvidiafb, nouveau
01:00.1 Audio device [0403]: NVIDIA Corporation GA104 High Definition Audio Controller [10de:228b] (rev a1)
Kernel driver in use: vfio-pci
Kernel modules: snd_hda_intelKernel modules に nouveau や snd_hda_intel が残っていても、実際に使用中のドライバが vfio-pci であれば問題ありません。見るべきは Kernel driver in use です。
Ubuntu Desktop VM の作成
PVE 上に Ubuntu Desktop VM を作成しました。
設定は以下です。
| 項目 | 値 |
|---|---|
| BIOS | OVMF |
| Machine | q35 |
| CPU type | host |
| Cores | 6 |
| Memory | 16384MB |
| System Disk | `local-lvm`, 80GB |
| ISO | `usb-1tb` 上の Ubuntu Desktop ISO |
| SCSI Controller | VirtIO SCSI single |
CPU type: host は、物理 CPU の機能をできるだけゲストに見せる設定です。単一ホストで GPU パススルー VM を使う今回の用途では、互換性重視の qemu より host の方が自然です。
Machine: q35 は、PCI Express 前提の比較的新しい仮想チップセットです。RTX 3060 Ti のような PCIe デバイスをパススルーするため、古い i440fx より q35 を選びました。
EFI Disk については、最初 pre-enrolled-keys=1 になっていましたが、NVIDIA ドライバ周りで Secure Boot が余計な変数になる可能性があるため外しました。

PVE で PCI Device を追加した際の起動失敗
Ubuntu Desktop VM は、デフォルト GPU の状態では起動できました。その後、VM を停止し、PCI Device として 01:00.0 と 01:00.1 を別々に追加したところ、VM が起動しなくなりました。

原因は、同一 GPU の multi-function デバイスを別々に追加したことだと判断しました。
RTX 3060 Ti は以下のように、同じ PCI スロット上の別 function として見えています。
01:00.0: GPU 本体01:00.1: HDMI / DisplayPort Audio
PVE では、01:00.0 のみを選び、All Functions と PCI-Express を有効にして 1 つの PCI Device として追加する方が素直でした。
実際にこの設定に変更すると、VM は起動できました。ゲスト内で lspci すると、GPU 本体と HDMI Audio の両方が認識されました。

ここで重要なのは、ゲスト内での PCI bus 番号がホストと一致する必要はないことです。ホストでは 01:00.0 でも、ゲスト内では 02:00.0 などとして見えることがあります。
ゲスト内で NVIDIA ドライバを導入
ゲストの Ubuntu Desktop 内で NVIDIA ドライバを導入しました。
sudo ubuntu-drivers install
sudo reboot再起動後、nvidia-smi は成功しました。
nvidia-smiただし、この時点では nvidia-smi は成功するものの、NVIDIA 側の物理ディスプレイには映像が出ませんでした。

nvidia-smi の表示では Disp.A Off になっていました。これは、GPU 自体はゲストで使えているものの、物理ディスプレイ出力が有効な表示先として扱われていない状態です。
物理ディスプレイに映らない問題の解決
解決策は、PVE 側の PCI Device 設定で passthrough GPU を Primary GPU にすることでした。
変更後、NVIDIA 側に接続した物理ディスプレイに Ubuntu Desktop VM の画面が表示されました。
今回の学びとして、nvidia-smi の成功は「ドライバが GPU を使えている」ことを示しますが、「物理端子から映像が出る」ことまでは保証しません。PVE 側で passthrough GPU を Primary GPU にする設定が必要でした。
実用検証
最終的に、PVE 上の Ubuntu Desktop VM で以下を確認しました。
- NVIDIA 側の物理ディスプレイ出力
- HDMI 音声出力
- USB ゲームパッド入力
- キーボード入力
- Google Chrome 上の Xbox Cloud Gaming / Fortnite
- 約 1 時間程度のゲームプレイ
- ゲームパッドの挿抜
結果として、映像、音声、入力ともに、物理マシンと違和感ない操作感でした。特に GPU パススルーによる実ディスプレイ出力は、一般的な VM コンソール画面のようなもやっとした表示ではなく、解像感も自然でした。
Windows VM についての判断
Windows VM については、GPU パススルーしない方針にしました。
理由は、Windows は稀に必要な Windows 専用アプリケーションを使うための補助用途だからです。RTX 3060 Ti を Linux Desktop VM と Windows VM で排他利用すること自体は可能ですが、同時起動できず、シャットダウン順序や GPU 解放、入力機器の切り替えなど運用コストが増えます。
今回の用途では、Windows VM は仮想 GPU + RDP / PVE コンソールで十分です。
Samba VM と 6TiB HDD の今後の方針
Samba 用の 6TiB HDD は既存データを消さずに使う前提です。PVE ホストに直接 Samba を入れるのではなく、Samba 専用 VM を作り、その VM に 6TiB HDD を渡す方針です。
PVE ホストは仮想化基盤に徹するべきです。ホストに Samba や Docker などを直接載せ始めると、責務分離が崩れ、トラブル時の切り分けが難しくなります。
Docker Hub image と PVE の LXC について
PVE が直接管理するコンテナは LXC です。Docker Hub の image をそのまま PVE の LXC コンテナとして一元管理するわけではありません。
Docker Hub の image を使う場合は、基本的には Linux VM を 1 台作り、その中で Docker を動かす方が素直です。LXC の中で Docker を動かすこともできますが、ネストしたコンテナ運用になるため、最初の本番構成では避ける判断が妥当です。
詰まった点と解決策まとめ
| 詰まった点 | 原因 | 解決策 |
|---|---|---|
| Ubuntu ホストで GPU を unbind できない | NVIDIA ドライバの usage count が残っていた | display manager や NVIDIA 関連サービスを止め、モジュールを外してから unbind |
| `nvidia-driver-580-open` 後に `nvidia-smi` が失敗 | ゲスト内 NVIDIA ドライバ構成が不十分だった可能性 | `ubuntu-drivers install` で推奨ドライバを導入 |
| PVE 上で dGPU が `nouveau` に掴まれる | 起動時に `vfio-pci` に早期 bind していなかった | `options vfio-pci ids=10de:2489,10de:228b` と blacklist、initramfs 更新 |
| PVE 上で PCI Device 追加後に VM が起動しない | GPU 本体と HDMI Audio を別々に追加した | `01:00.0` を 1 つ追加し、`All Functions` + `PCI-Express` を有効化 |
| `nvidia-smi` は成功するが物理画面が出ない | passthrough GPU が VM の primary display になっていなかった | PCI Device 設定で `Primary GPU` を有効化 |
| iGPU 側に DMAR fault が出る | PVE 上で iGPU も IOMMU 管理対象になっていた | 実害がなければ様子見。必要なら `intel_iommu=igfx_off` を検討 |この記事から得られる知見
今回の構成では、GPU パススルーの成否を決める重要要素は、ソフトウェア設定だけではありませんでした。むしろ、ハードウェアの素直さが大きく効いています。
特に重要だったのは以下です。
- iGPU と dGPU を分けられること
- dGPU と HDMI Audio が素直な IOMMU グループにいること
- PVE ホストが dGPU を掴まず
vfio-pciに渡せること - PVE では multi-function GPU を
All Functions付きで 1 つの PCI Device として扱うこと nvidia-smi成功後も、物理出力にはPrimary GPU設定が必要な場合があること
今回のマシンは、GPU パススルー用途としてかなり当たりの構成でした。
今後の課題
今後進める予定の作業は以下です。
- Samba VM を作成し、6TiB HDD を安全に公開する
- Windows VM を仮想 GPU + RDP 前提で作成する
- PVE ホスト設定、Linux Desktop VM、Samba VM のバックアップ方針を決める
- 外付け SSD 500GiB x2 の使い道を、バックアップ用、検証用などに整理する
- 必要に応じて Docker 用の軽量 Linux VM を作る
参考文献
- Proxmox VE PCI Passthrough: https://pve.proxmox.com/wiki/PCI_Passthrough
- Proxmox VE Storage: https://pve.proxmox.com/pve-docs/chapter-pvesm.html
- Proxmox VE Linux Container: https://pve.proxmox.com/pve-docs/chapter-pct.html
- Linux VFIO driver documentation: https://docs.kernel.org/driver-api/vfio.html
- Linux kernel parameters: https://docs.kernel.org/admin-guide/kernel-parameters.html
- Ubuntu NVIDIA drivers installation: https://ubuntu.com/server/docs/nvidia-drivers-installation