📦

Ollama+OpenWebUI+SearXNGをProxmoxのLXCだけで建てる

に公開

ProxmoxのLXCでWeb検索もできるAIチャットを組んでみたよってお話です。
VMを立てて構築する記事が多いですが私は自宅サーバーが貧弱なので全部LXCで建ててオーバーヘッドを減らそうとしたら幸運にもうまく動作したので共有します。

構成

今回は

  • Ollamaコンテナ(10vCPU+24GB RAM+SSD 64GB)
  • Dockerコンテナ(1vCPU+1GB RAM+SSD 16GB)
    この2台のLXCで動かそうと思います。
    DockerはVMで運用することが推奨されていますが、2コンテナ程度であればLXCのコンフィグを弄るだけで安定して動くので「これでいいや」って感じです(テキトー)

ちなみにホストのスペックは

  • AMD Ryzen5 5600G
  • DDR4-3200 32GB
  • 1TB PCIe3.0x4 SSD
    です。

1. Ollamaのセットアップ

ProxmoxのWebUIでDebianのコンテナイメージをダウンロードしてできるだけ多くのリソースを割り当てて作成します。
Ollamaで使用できるdGPUがある場合にはパススルーしておくと良いでしょう。

apt update && apt upgrade -y && apt install -y curl lshw

パッケージをアップデートしcurlとlshwをインストールします。
OllamaはGPUドライバーのインストールにlspciまたはlshwでハードウェアを参照します。

curl -fsSL https://ollama.com/install.sh | sh

Ollamaのインストールシェルを走らせます。アップデート時にもこのコマンドを使用します。

systemctl edit ollama

ここが味噌です。
サービスファイルに環境変数を追記しますがollama.serviceには直接書きこみません。
先程のコマンドでアップデートした際に追記した部分が上書きで消えてしまうからです。
systemctlにはeditコマンドがありこれを使用するとollama.service.d/override.confが作成され元ファイルに変更を加えることなく文字通りオーバーライドして定義してくれます。

### Editing /etc/systemd/system/ollama.service.d/override.conf
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
Environment="OLLAMA_HOST=0.0.0.0"
Environment="OLLAMA_ORIGINS=192.168.0.*"

### Edits below this comment will be discarded


### /etc/systemd/system/ollama.service
# [Unit]
# Description=Ollama Service
# After=network-online.target
# 
# [Service]
# ExecStart=/usr/local/bin/ollama serve
# User=ollama
# Group=ollama
# Restart=always
# RestartSec=3
# Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# 
# [Install]
# WantedBy=default.target

OLLAMA_ORIGINS環境変数は適宜使用するネットワークに応じて書き換えてください。

Successfully installed edited file '/etc/systemd/system/ollama.service.d/override.conf'.

保存後に上記の表示が出れば問題ありません。

systemctl daemon-reload && systemctl restart ollama.service

サービスファイルを再読み込みしOllamaを再起動します。

systemctl status ollama.service 

* ollama.service - Ollama Service
     Loaded: loaded (/etc/systemd/system/ollama.service; enabled; preset: enabled)
    Drop-In: /etc/systemd/system/ollama.service.d
             `-override.conf
     Active: active (running) since Wed 2025-11-05 16:38:12 UTC; 8s ago
 Invocation: 0e7549c6260d442c8fa711c3769a8973
   Main PID: 1074 (ollama)
      Tasks: 10 (limit: 37563)
     Memory: 9.7M (peak: 32.8M)
        CPU: 109ms
     CGroup: /system.slice/ollama.service
             `-1074 /usr/local/bin/ollama serve

Nov 05 16:38:12 ollama ollama[1074]: time=2025-11-05T16:38:12.984Z level=INFO source=routes.go:1524 msg="server config" env="map[CUDA_VISIBLE_DEVICES: GGML_VK_VISIBLE_DEVICES: GPU_DEVICE_ORDINAL: HIP_VISIBLE_DEVICES: HSA>
Nov 05 16:38:12 ollama ollama[1074]: time=2025-11-05T16:38:12.984Z level=INFO source=images.go:522 msg="total blobs: 0"
Nov 05 16:38:12 ollama ollama[1074]: time=2025-11-05T16:38:12.984Z level=INFO source=images.go:529 msg="total unused blobs removed: 0"
Nov 05 16:38:12 ollama ollama[1074]: time=2025-11-05T16:38:12.984Z level=INFO source=routes.go:1577 msg="Listening on [::]:11434 (version 0.12.9)"
Nov 05 16:38:12 ollama ollama[1074]: time=2025-11-05T16:38:12.984Z level=INFO source=runner.go:76 msg="discovering available GPUs..."
Nov 05 16:38:12 ollama ollama[1074]: time=2025-11-05T16:38:12.985Z level=INFO source=server.go:400 msg="starting runner" cmd="/usr/local/bin/ollama runner --ollama-engine --port 34141"
Nov 05 16:38:13 ollama ollama[1074]: time=2025-11-05T16:38:13.002Z level=INFO source=server.go:400 msg="starting runner" cmd="/usr/local/bin/ollama runner --ollama-engine --port 38613"
Nov 05 16:38:13 ollama ollama[1074]: time=2025-11-05T16:38:13.019Z level=INFO source=server.go:400 msg="starting runner" cmd="/usr/local/bin/ollama runner --ollama-engine --port 34123"
Nov 05 16:38:13 ollama ollama[1074]: time=2025-11-05T16:38:13.067Z level=INFO source=types.go:60 msg="inference compute" id=cpu library=cpu compute="" name=cpu description=cpu libdirs=ollama driver="" pci_id="" type="" t>
Nov 05 16:38:13 ollama ollama[1074]: time=2025-11-05T16:38:13.067Z level=INFO source=routes.go:1618 msg="entering low vram mode" "total vram"="0 B" threshold="20.0 GiB"

Listening on [::]:11434となっていれば定義したネットワーク内で要求を受け付けていることになります。
心配であればProxmoxのファイアウォールも定義しておきましょう。

2. OpenWebUI+SearXNGのセットアップ

フロントエンドにはOpenWebUIを使い、LLMモデルが検索をする際のエンジンとしてSearXNGを用います。
外部のAPIを使わないので使い放題です。(常識的な用法用量を守りましょう)
同じくDebianでコンテナを作成します。OpenWebUIが少しメモリを食うので1GBくらいに設定するといいかもしれません。ディスクは増分は出来ても減らすことができないので少なめから始めましょう。

コンテナ作成後、起動はせずにホストの/etc/pve/lxc/<コンテナID>.confに下記を追記します。
Docker composeで2つ以上のコンテナを定義した際に必要です。

lxc.apparmor.profile: unconfined
lxc.cap.drop:
lxc.cgroup2.devices.allow: a

コンテナを起動したらDockerをインストールします。

apt update && apt upgrade -y && apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

お好みのディレクトリにcompose.yamlを作成します。

mkdir -p docker/openwebui/
cd !$
nano compose.yaml
services:
  openwebui:
    container_name: openwebui
    image: ghcr.io/open-webui/open-webui:main
    environment:
      RAG_OLLAMA_BASE_URL: "http://<Ollamaコンテナのアドレス>:11434"
      ENABLE_RAG_WEB_SEARCH: True  
      RAG_WEB_SEARCH_ENGINE: "searxng"  
      RAG_WEB_SEARCH_RESULT_COUNT: 3  
      RAG_WEB_SEARCH_CONCURRENT_REQUESTS: 10
      SEARXNG_QUERY_URL: "http://searxng:8080/search?lang=ja&q=<query>"
    ports:
      - "3000:8080"
    volumes:
      - open-webui:/app/backend/data
    restart: always

  searxng:
    container_name: searxng
    image: searxng/searxng:latest
    ports:
      - "8080:8080"
    volumes:
      - ./searxng:/etc/searxng:rw
    restart: unless-stopped
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
      - DAC_OVERRIDE
    logging:
      driver: "json-file"
      options:
        max-size: "1m"
        max-file: "1"

volumes:
  open-webui:

作成したら一度コンテナを起動し、SearXNGのコンフィグを生成します。
検索結果の受け渡しはjsonを使用するのでsedで対応フォーマットを追記します。

docker compose up -d ; sleep 10 ; docker compose down
sed -i 's/formats: \[\"html\"\/]/formats: [\"html\", \"json\"]/' searxng/settings.yml
docker compose up -d

OpenWebUIドキュメント
これでWeb検索対応のOpenWebUIが立ち上がってあるはずです。
ここまでの設定は必要最小限なので各自自分に合った環境にチューニングしてみてください。

gpt-oss:20bでAI検索してみる

特にネタが思いつかないので1週間の天気を表にしてもらいました(ローカルLLMを使う意味)
gpt-ossでAI検索

最後に

情報は執筆時点のものなので最新のドキュメントなどを確認しつつ構築してください。

つよいぐらぼがほしい

Discussion