vast.ai で RTX 5090 を借りて ローカル LLM を動かす|2026-08 版
第 14 章

【実践】llama.cpp と GGUF で動かす

量子化済みモデルを 32 GB に収める実務。llama-server で API 化まで

この章で分かること

GGUF は「重み 1 ファイル」、llama-server はそれを配る HTTP サーバー

量子化(= 重みの数値を粗く丸めてファイルを小さくする加工)済みのモデルは、GGUF という形式の .gguf ファイルとして配布されています。llama.cpp はそれを読んで動かすプログラム群で、この章で使う llama-server はそのうちの 1 つです。公式の自己紹介は「httplib・nlohmann::json・llama.cpp をベースにした、高速・軽量・純粋な C/C++ の HTTP サーバー」。OpenAI API 互換の chat completions / responses / embeddings のルートを持ち、Anthropic の Messages API 互換のルートも持ちます。連続バッチング(= 複数のリクエストをまとめて同時に処理する仕組み)と Web UI は既定で有効です。

vast.ai の公式ドキュメントが GGUF を扱うときに llama.cpp を推す理由は 1 つで、「多数の分割ファイル(split GGUF)をマージせずに直接ロードできる」からです。大きなモデルは DeepSeek-R1.Q8_0-00001-of-00015.gguf のように 15 分割で配布されることがありますが、--model に渡すのは 先頭の -00001-of-00015 だけで、残りは自動で読まれます。結合作業もそのための空きディスクも要りません。

ファイルの置き方には決まりがあります。--models-dir にディレクトリを渡す場合、単一ファイルのモデルは直下に置き、マルチモーダルやマルチシャード(分割)のモデルはサブディレクトリに入れます。画像を扱うための projector ファイルは、名前が mmproj で始まっている必要があります。

models_directory
 ├─ Qwen3-8B-Q4_K_M.gguf
 ├─ gemma-3-4b-it-Q8_0/
 │    ├─ gemma-3-4b-it-Q8_0.gguf
 │    └─ mmproj-F16.gguf
 └─ Kimi-K2-Thinking-UD-IQ1_S/
      ├─ Kimi-K2-Thinking-UD-IQ1_S-00001-of-00006.gguf
      └─ ... -00006-of-00006.gguf

なお vast.ai の GGUF ガイドが例に使っている DeepSeek-R1 の Q8_0 は、公式が「VRAM 800 GB 以上・ストレージ 700 GB・推奨は 8×H200」と明記しているモデルです。1 枚の RTX 5090(32 GB)ではまったく動きません。本章は同じ手順を、32 GB に入るサイズのモデルで踏みます。

では「32 GB に入るサイズ」をどう見分けるか。目安は GGUF ファイルの合計サイズです。いま挙げた DeepSeek-R1 Q8_0 が「ストレージ 700 GB/VRAM 800 GB」だったように、全レイヤーを GPU に載せる構成では、ファイルサイズと同程度かそれより少し多い VRAM が要ります(必要 VRAM の計算式そのものは llama.cpp の公式ドキュメントにありません。この目安は vast.ai が挙げた 2 つの数値の関係から読み取ったものです)。計算例: 5090 の VRAM は 32607 MB(2026-08-24 時点の実測値)で、ここから後述する --fit の余白(既定 1024 MiB)と、会話が伸びるほど増える KV キャッシュ(= 入力と生成済みトークンぶんの計算結果を取っておく領域)のぶんを引く必要があります。ファイルが 30 GB を超える GGUF は、1 枚では全レイヤーを載せきれないと考えてください。

Hugging Face の GGUF リポジトリには Q4_K_M・Q5_K_M・Q8_0 といった量子化違いが同じ場所に同居していることが多く、リポジトリ全体を落とすと数百 GB になります。第 11 章でやったとおり、hf download <ユーザー名>/<リポジトリ名> --include "*Q4_K_M*" のように欲しい 1 種類だけを名指しで落とし、その前に --dry-run を付けて何 GB になるかを見てから実行してください(<ユーザー名>/<リポジトリ名> は Hugging Face のモデルページの URL に出ている 2 語です)。ディスクは借りるときに決めた容量ぶん課金されます。単価は 2026-08-24 時点の実測で中央値 0.200 USD/GB/月 なので、計算例として 40 GB を 1 か月確保すると 40 × 0.200 = 月 8 USD です。

手に入れる道は 3 つ

ルート起点になるコマンド向き・不向き
A. vast.ai のテンプレート同梱の既製バイナリllama-server ...(最初から PATH 上にある)何も入れなくてよい。vast.ai 固有なので、テンプレを離れると再現しない
B. 公式 Docker イメージdocker run --gpus all ... ghcr.io/ggml-org/llama.cpp:server-cudaビルド不要で数分。ただし公式が「GPU 対応イメージはビルドが通ること以外 CI でテストしていない」と明記している
C. 自分で CUDA ビルドcmake -B build -DGGML_CUDA=ON最新のコードを使える。CUDA Toolkit が要り、ビルド時間もかかる

vast.ai が推しているのは A です。Open WebUI テンプレートには、プリコンパイル済みで CUDA 対応の llama-server と llama-cli が最初から入っています。モデルの置き場所は /workspace/llama.cpp/models で、同梱スクリプト llama-dl.sh --repo <ユーザー名>/<リポジトリ名> --version <量子化タイプ> で落とせます(<...> は Hugging Face 上のモデルの持ち主・名前・Q4_K_M のような量子化タイプ)。

B を選ぶときは、タグ(= イメージの版を表す名札)の選び方が 1 か所だけ重要です。NVIDIA GPU で使うのは server-cuda(CUDA 12 版)か server-cuda13(CUDA 13 版)で、バックエンド名の付かない server は GPU 向けにコンパイルされていない版です(公式ドキュメントもこのタグを CPU の例として載せています)。--gpus all は「ホストの GPU を全部コンテナに渡す」という Docker への指示で、GPU 対応のクラウドを使っているなら、これで cuBLAS(= NVIDIA が配っている行列計算のライブラリ)がコンテナの中から使えるようになります。

docker run -p 8080:8080 -v <モデルを置いたフォルダ>:/models --gpus all \
  ghcr.io/ggml-org/llama.cpp:server-cuda \
  -m /models/<モデルファイル名>.gguf -c 2048 --host 0.0.0.0 --port 8080

<モデルを置いたフォルダ> はインスタンス側で .gguf を置いたディレクトリの絶対パス(例: /workspace/models)、<モデルファイル名>.gguf はその中のファイル名(分割されているなら -00001-of-... の 1 本目)です。-v の左がホスト側、右がコンテナの中から見えるパスなので、-m に渡すのはコンテナ側の /models/... になります。-v を付け忘れるとコンテナの中にモデルが無く、コンテナを消すとダウンロードしたモデルも一緒に消えます。なお公式の例には --n-gpu-layers 99 が付いていますが、いまの -ngl の既定は auto なので省いてあります(次節)。

C を選ぶなら、Blackwell 向けの指定を 1 つ覚えます。llama.cpp は既定で「いまつながっている GPU 向け」にビルドされ、nvcc が GPU を見つけられないと nvcc warning : Cannot find valid GPU for '-arch=native', default arch is used と警告します。対処は CUDA アーキテクチャを明示することです。公式が載せている例は -DCMAKE_CUDA_ARCHITECTURES="86;89" で、これは Compute Capability(= GPU の世代・機能水準を表す番号。NVIDIA が製品ごとに公表しています)8.6 と 8.9 を並べたもの。つまり小数点を取って書き、複数あればセミコロンで区切るという規則がここから読み取れます。NVIDIA の一覧表は GeForce RTX 5090 を Compute Capability 12.0 に分類しているので、指定は "120" になります(vast.ai の offer が返す 5090 のフィールドも全件 compute_cap = 1200 でした。2026-08-24 時点の実測)。この "120" という値自体は llama.cpp の公式ドキュメントには載っていません。公式の例から読み取れる規則と、NVIDIA の表を突き合わせて出した値です。

cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="120"
cmake --build build --config Release -t llama-server

できあがるのは ./build/bin/llama-server です。-DGGML_NATIVE=OFF を付ければ全 CUDA GPU 向けの汎用バイナリになりますが、公式が「バイナリが大きくなり、コンパイルに長時間かかる」と書いているとおり代償があります。借りるたびにホストが変わる vast.ai では、そのインスタンスの上でビルドして、そのインスタンスで使い切るのが素直です。古い CUDA の環境では cospi などの宣言が衝突してビルドが壊れる既知の問題もあるため、新しい CUDA のホストを選ぶこと自体が対策になります(5090 のホストは実測で cuda_max_good が 12.8〜13.3 に分布し、12.8 未満は 1 件もありませんでした。2026-08-24 時点)。なお Blackwell に必要な最低 CUDA バージョンは llama.cpp の公式ドキュメントに明記がありません。手がかりは、公式 Docker イメージのビルド既定値が CUDA_VERSION=12.8.1 であることと、この実測分布の 2 つだけです。

vast.ai のドキュメントが載せている自前ビルドのレシピは、公式の手順に 1 つだけ足しています。apt-get install libcurl4-openssl-dev を先に実行し、-DLLAMA_CURL=ON を付けてビルドする形です。この curl 関連の指定は llama.cpp 公式の docs/build.md には出てきません。後述する -hf(ネットワーク越しにモデルを取る指定)が curl 無しで動くかどうかも公式ドキュメントに明記がないので、-hf を使いたいならテンプレート同梱の既製バイナリか公式 Docker イメージを選ぶのが確実です。

apt-get install libcurl4-openssl-dev
cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="120" -DLLAMA_CURL=ON
cmake --build build --config Release -j 8 -t llama-server

この 3 行は、1 つ前の 2 行と入れ替えて使います(-hf を使うつもりが無いなら、前の 2 行のままで構いません)。-j 8 は「8 並列でコンパイルする」という指定で、公式が例示している書き方です。数字は借りたマシンの CPU コア数に合わせてください。vast.ai のインスタンスには root で入るので sudo は要りません。ビルドしている間も GPU の時間課金は走っています。

立てる — 「起動した」と「使える」は別

最小の起動は llama-server -m <モデルファイルのパス> -c 2048 で、これで 既定では 127.0.0.1:8080 を listen するサーバーが立ちます。ただしコマンドを打った瞬間に使えるようになるわけではありません。モデルを読み込んでいる間、/health は 503 と {"error": {"code": 503, "message": "Loading model", "type": "unavailable_error"}} を返し続け、読み終えて初めて 200 と {"status": "ok" } になります。この /health だけは API キーのチェックがかからない公開エンドポイント(= URL の末尾に付ける、用途ごとの入り口のこと)なので、認証を設定した後でも待ち合わせに使えます。同じものが /v1/health でも叩けます。

この間ずっと GPU の時間課金が走っている(0.40 USD/時) llama-server 起動 モデルを VRAM へ読み込み中 使える状態 /health まだ何も返らない 503 Loading model 200 status ok 経過時間(モデルが大きいほど長い) curl http://localhost:20000/health を繰り返して待つ
図 14-1 待ち時間の長さはモデルのサイズと回線で決まります。金額は 2026-08-24 時点の 1×RTX 5090 の中央値 0.4037 USD/時 で計算した目安です(1 USD = 150 円として約 61 円/時)。
やってみる 14-1|llama-server を立てて /health が 200 になるまで

SSH で入った直後から始めます。所要 15 分ほど、この間の課金は約 0.10 USD(0.4037 USD/時 で計算。2026-08-24 時点)。モデルのダウンロード時間は別途かかります。

  1. GPU がドライバから見えているか確かめる。RTX 5090 の行と VRAM が出れば OK。
  2. モデルを用意する。第 11 章の hf download で .gguf を落としておくのが確実です(テンプレート同梱の llama-dl.sh でも構いません)。
  3. tmux の別ウィンドウ(Ctrl+b → c)でサーバーを起動する。<モデルファイルのパス> は手順 2 で落とした .gguf の絶対パス、分割ファイルなら -00001-of-... の 1 本目を指定します。
  4. 元のウィンドウに戻り(Ctrl+b → n)、/health を叩く。503 の間は待ちます。
  5. 200 になったら OpenAI 互換の口を叩いて、返事の JSON が返ってくることを確認する。
nvidia-smi
llama-server --model <モデルファイルのパス> --ctx-size 8192 --port 20000
curl http://localhost:20000/health
curl -X POST http://localhost:20000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "自己紹介を 1 文で"}]}'

リクエストに model を書いていないのは、このサーバーに --model で指定した 1 つしかモデルが載っていないからです(/v1/models が返す一覧は常に 1 件だと公式が明記しています)。クライアント側が model の指定を必須にしている場合は、公式の Python 例が davinci-002 という無関係な名前を渡しているとおり、文字列は何でも構いません。API から見える名前を自分で決めたいなら -a, --alias <好きな名前> を付けます。Authorization ヘッダを付けていないのは、サーバー側で --api-key を設定していないうちは認証がかからないからです(設定した後は -H "Authorization: Bearer <決めたキー>" を足します)。

ポートを 20000 にしているのは任意の数字ではありません。vast.ai の Open WebUI テンプレートが「http://localhost:20000 で動いている OpenAI 互換 API を探す」よう事前設定されているからです。別の番号にすると、あとで Open WebUI のモデル一覧に出てきません。--ctx-size(= 一度に扱える文脈の長さ)を明示しているのは次節の理由からです。

ネットワーク経由でモデルを取る -hf <ユーザー名>/<リポジトリ名>:<量子化タイプ> という指定もあります(公式の例は ggml-org/GLM-4.7-Flash-GGUF:Q4_K_M)。手軽ですが、:量子化タイプ を省くと既定で Q4_K_M が選ばれ、それが無いリポジトリでは「中の最初のファイル」にフォールバックします。1-bit 量子化や巨大な F16 を引き当てることがあるので、使うなら必ず明示してください。落としたファイルは LLAMA_CACHE 環境変数が指すキャッシュに置かれ、一覧は llama-server -cl で見られます。Llama 系のような gated(= 利用申請と承認が要る)リポジトリでは、HF_TOKEN 環境変数か -hft <トークン> でトークンを渡さないと落ちます。

32 GB に収める — 回すつまみは 4 つ

VRAM(= GPU 自身のメモリ)に載り切らないモデルでも、llama.cpp は動きます。-ngl, --gpu-layers, --n-gpu-layers が「VRAM に置くレイヤーの最大数」を決めていて、そこに入らなかったレイヤー(= モデルを構成する層。トークンを 1 個作るたびに上から下まで順に通ります)は CPU 側のシステムメモリに置かれ、CPU が計算するからです。取れる値は「正確な数値」「auto」「all」で、既定は auto。古い記事に出てくる -ngl 99 は「全部載せろ」の慣用表現で、いまは必須ではありません。

VRAM 32607 MB(RTX 5090) システム RAM(CPU が計算) 1 トークンごと ① VRAM に載った層 GPU が計算する。ここが速い -ngl が決めるのはこの境界の位置 ② CPU に落ちた層 システム RAM 上で CPU が計算する。遅い 落ちた層の数だけ 1 トークンが遅くなる
図 14-2 トークンを 1 個作るたびに、モデルの全レイヤーを上から下へ 1 往復します。境界を跨ぐ回数は 1 トークンにつき決まった数だけ発生するので、CPU 側に落ちた層の分だけ生成が遅くなります。

ここで -ngl 0 にしても GPU が完全に切れるわけではない点に注意してください。公式は「-ngl 0 を使っていても、計算の一部を高速化するために GPU は依然として使われることがある。完全に無効化するには --device none を使う」と明記しています。

もう 1 つ、既定で勝手に働く仕組みがあります。-fit, --fit [on|off] は「指定しなかった引数を、デバイスメモリに収まるように調整する」機能で、既定は on です。つまり何も指定しなければ、llama.cpp のほうが値を勝手に下げて起動を通します。「動いたけれど、思っていたよりコンテキストが短かった」の正体はこれです。関連する引数が 2 つあり、空けておく余白は -fitt, --fit-target の既定で 1024 MiB(メビバイト。1024 MiB ≒ 1.07 GB)、--fit が下げてよいコンテキストの下限は -fitc, --fit-ctx の既定で 4096 です。逆に言えば、自分で明示した引数は調整の対象になりません。

ここで 1 つ、公式ドキュメントが答えていない点があります。--fit がどの引数を調整対象にするかの一覧は書かれていません。コンテキスト長(-c)が対象に入ることは、-fitc が「--fit が設定しうる最小 ctx サイズ」と定義されていることから確実です。-ngl も「未設定の引数を収まるように調整する」という説明の範囲に入ると読めますが、そこまでは明示されていません。だからこそ再現性が要る場面では -c と -ngl を自分で書くのが安全です。書いた値は動かされません。

VRAM 32607 MB(RTX 5090・2026-08-24 時点の実測) モデルの重み ≒ GGUF のサイズ KV キャッシュ --fit の余白 小さい量子化を選ぶ・-ngl を下げる -c を下げる・-ctk/-ctv を q8_0 に -fitt 既定 1024 MiB 収まらないとき: --fit(既定 on)が、指定していない引数を勝手に下げて収める = 「動いたのにコンテキストが 4096 まで縮んでいた」の正体(-fitc の既定が 4096) 明示した引数は調整されない = 自分で決めた値は守られる
図 14-3 帯の幅は模式で、実際の比率ではありません。読み方は「32 GB は重みだけのための枠ではない」という 1 点です。KV キャッシュは会話が長くなるほど育ちます。
つまみ既定効く先
-ngl, --n-gpu-layers NautoVRAM に置くレイヤー数。下げると CPU に逃げるぶん遅くなる
-fit, --fit [on|off]on指定しなかった引数を自動で下げる。-fitt(既定 1024 MiB)・-fitc(既定 4096)が付属
-c, --ctx-size N0コンテキスト長。KV キャッシュの大きさを直接決める。0 は「無制限」ではなくモデル側の値を使う
-ctk / -ctv TYPEf16KV キャッシュの型。f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1 から選ぶ

それでも溢れたときの既定の挙動はクラッシュです。Linux では GGML_CUDA_ENABLE_UNIFIED_MEMORY=1 を付けて起動すると、「GPU の VRAM を使い切ったときにクラッシュせずシステム RAM へスワップできる」ようになります。落ちるよりはましですが、遅くなることと引き換えである点は変わりません。MoE(= 層の一部だけを使い分ける構造)のモデルなら、-cmoe(その重みを全部 CPU へ)や -ncmoe N(先頭 N 層分だけ CPU へ)という逃がし方もあります。

やってみる 14-2|収まらないときにつまみを回す

やってみる 14-1 のサーバーを Ctrl+c で止めてから始めます。所要 10 分、課金は約 0.07 USD(0.4037 USD/時 で計算。2026-08-24 時点)。

  1. まずコンテキストと KV キャッシュを削る。-c 4096 と KV キャッシュの q8_0 化を同時に効かせます。
  2. ターミナルに流れる起動ログを最後まで見る。エラーで止まらず /health が 200 になれば、その設定は VRAM に収まっています。何が起きているかを細かく見たいときは -lv 4(ログ閾値を trace まで上げる指定。既定は 3 = info)を足します。
  3. それでも落ちるなら -ngl に具体的な数字を入れて、GPU に載せる層を減らす。<層数> はモデルの総レイヤー数より小さい整数で、まずは 20 のような小さめの値から始め、動いたら増やして限界を探ります。
  4. クラッシュだけは避けたいときは unified memory を有効にして起動する。
  5. 別ウィンドウで nvidia-smi を叩き、VRAM の使用量が 32607 MB(= 32 GB。2026-08-24 時点の実測値)に対してどこまで来ているかを見る。
llama-server --model <モデルファイルのパス> --port 20000 -c 4096 -ctk q8_0 -ctv q8_0
llama-server --model <モデルファイルのパス> --port 20000 -c 4096 -ngl <層数>
GGML_CUDA_ENABLE_UNIFIED_MEMORY=1 llama-server --model <モデルファイルのパス> --port 20000

/health が 200 を返し、nvidia-smi の使用量が VRAM の総量を下回っていれば成功です。生成が異様に遅いときは、図 14-2 の②に落ちた層があると考えてください。-c をさらに削るか、1 段小さい量子化のファイルに替えて VRAM に余裕を作れば、GPU に載る層が増えます。

外から使う・Open WebUI に繋ぐ・速度を測る

--host の既定は 127.0.0.1 です。この既定のままでは、インスタンスの外からは 1 バイトも届きません。vast.ai のガイドの起動例に --host が出てこないのは、同じインスタンスの中で動く Open WebUI から localhost 経由で繋ぐ前提だからです。

vast.ai のインスタンス(1×RTX 5090) 手元の Mac / PC トンネル llama-server --port 20000 localhost Open WebUI http://localhost:20000/v1 --host の既定 127.0.0.1 = 同じインスタンスの中からしか届かない 外に直接開くなら --host 0.0.0.0 と --api-key をセットで。開けないなら SSH トンネルのままでよい
図 14-4 Open WebUI の設定画面に入れる URL は、この図の localhost の矢印そのものです。Open WebUI 自身が Docker の中にいる場合だけ、localhost を host.docker.internal に置き換えます。

Open WebUI 側の設定は ⚙️ Admin Settings → Connections → OpenAI → ➕ Add Connection で、入力欄は次の 3 つです(実際に埋めるのは上の 2 つだけ)。

フィールド入れる値
URLhttp://localhost:20000/v1(公式の例は http://127.0.0.1:10000/v1。Open WebUI が Docker の中なら http://host.docker.internal:20000/v1)
API Keynone(サーバー側で --api-key を設定していないなら空欄でよい)
Model IDs (Filter)任意。空のままでよい。モデル一覧の取得に失敗するときだけ、ここにモデル ID を打ち込んで + を押して手で登録する

末尾の /v1 は必須です。公式ドキュメントが「Note the /v1 at the end!!」と感嘆符 2 つで書いているほど、ここが最頻の失敗です。Open WebUI は接続を検証するときにプロバイダの /models を標準の Bearer トークンで呼びますが、この検証が赤くなっても会話は動くことがあります。公式も「/models を実装していない、または非標準の認証を使うプロバイダでは検証がエラーになるが、それは非互換を意味しない。chat completions は動く」と書いています。その場合は表の Model IDs (Filter) にモデル ID を手で足してください。サーバーの起動が遅くて検証がタイムアウトする場合は、Open WebUI 側の環境変数 AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST=30(既定は 10 秒)で窓を広げられます。モデル一覧に出てこないときは、まずページをリロードしてください。

インスタンスの外へ直接開くなら、まず第 10 章の手順でそのポートを外向きに公開したうえで、--host 0.0.0.0 にし、--api-key <自分で決めた長い文字列> をセットで付けます。認証に失敗したリクエストには 401 と Invalid API Key が返ります。CORS(= ブラウザが「どのサイトの JavaScript にこの API を叩かせてよいか」を判定する仕組み)の既定が緩い点も知っておいてください。公式は「既定ではサーバーは任意の Origin ヘッダをそのまま反射し、credentials を許可する」と明記しています。公式が挙げている推奨は 3 段階で、同じマシンからしか使わないなら --cors-origins localhost、社内 LAN なら使うフロントエンドの origin を書く、公開するなら API キーを設定したうえでリバースプロキシの背後に置く、です。Web UI の実験的な機能(--tools や MCP 関連)は「信頼できない環境で有効にするな」と公式が繰り返し警告しているので、公開 IP を持つ vast.ai のインスタンスでは触らないのが安全です。

やってみる 14-3|手元から叩いて、速度を数字で出す

所要 10 分、課金は約 0.07 USD(0.4037 USD/時 で計算。2026-08-24 時点)。ポートを外に開かず、SSH トンネル(= 手元のポートをインスタンスのポートに繋ぐ通り道)だけで完結させます。

  1. インスタンス側で --metrics を付けて起動し直す。これで /metrics が有効になります。Prometheus(= 定番の監視ツール)が読める、1 行 1 指標のテキストを返すエンドポイントです。付けずに叩くと 501 と This server does not support metrics endpoint. が返ります。
  2. 手元のターミナルでトンネル付きで接続する。<ポート> と <ホスト> はインスタンスカードの SSH ボタンから写します。そのウィンドウは繋いだまま放置します。
  3. 手元の別のターミナルから、自分の localhost:20000 を叩く。ブラウザで http://localhost:20000 を開けば、内蔵の Web UI がそのまま出ます(既定で有効)。
  4. 何度か会話したあとで /metrics を読む。
llama-server --model <モデルファイルのパス> --port 20000 --metrics
ssh -p <ポート> root@<ホスト> -L 20000:localhost:20000
curl -s http://localhost:20000/metrics | grep llamacpp:predicted_tokens_seconds

llamacpp:predicted_tokens_seconds が平均の生成スループット(トークン/秒)です。プロンプト処理側は llamacpp:prompt_tokens_seconds に出ます。この 2 つが、借りた RTX 5090 で「何 tokens/s 出たか」を語るときの、公式に用意された計測手段です。設定を変えたら同じ手順で測り直して比べてください。

つまずきポイント

--sleep-idle-seconds で寝かせれば課金が止まると思っていた

止まりません。--sleep-idle-seconds <秒>(既定 -1 = 無効)は、無操作が続いたときにモデルと KV キャッシュを RAM からアンロードするだけの機能です。GPU のレンタル料金はインスタンスが動いている限り走り続けます。お金を止めるのは vast.ai 側の stop(GPU 代は止まるがディスク代は続く)と destroy だけです。なお /health・/props・/models・/metrics の 4 つは「仕事」とみなされないので、これらを叩いてもモデルは再ロードされず、アイドルタイマーもリセットされません。

-c 0 を「コンテキスト無制限」だと思っていた

-c, --ctx-size の既定 0 の意味は「モデルからロードした値を使う」です。無制限ではありません。学習時のコンテキスト長が長いモデルでこれをそのままにすると、KV キャッシュが VRAM を食い潰します。「長く生成し続ける」ほうも要注意で、--context-shift は既定で無効です。無限に書き続けさせるつもりでいると、ある時点で止まります。

--host 0.0.0.0 にしただけで満足してしまった

vast.ai のインスタンスはグローバル IP を持ちます。0.0.0.0 で待ち受けるということは、そのポートに到達できる全員があなたの GPU を使えるということです。--api-key は必ずセットで付けてください(カンマ区切りで複数指定でき、--api-key-file なら 1 行 1 個のファイルで管理できます。# で始まる行はコメント扱い)。あわせて CORS の既定が「任意の Origin を反射して credentials を許可する」ことも思い出してください。

フラグの書式を古い記事から写して起動に失敗する

3 つよくあります。① -fa(Flash Attention = 注意機構の計算をメモリ効率よく行う実装)は on / off / auto を取る引数で、既定は auto。-fa 1 という書式は公式にありません。② -b(論理バッチ、既定 2048)と -ub(物理バッチ、既定 512)は別物です。③ --mlock / --mmap / --no-mmap は deprecated で、いまは -lm, --load-mode に統合されています。迷ったら環境変数でも同じ設定ができます(LLAMA_ARG_MODEL・LLAMA_ARG_CTX_SIZE など)。ただしコマンドライン引数のほうが環境変数より優先されるので、両方書いて食い違うと引数が勝ちます。

理解度チェック

Q1. llama-server のコマンドを打って数秒後、curl /health が 503 を返しました。壊れていますか?

壊れていません。503 の本文は Loading model で、モデルをまだ読み込み中という意味です。読み終えると 200 と {"status": "ok" } に変わります。/health は API キーのチェックがかからない公開エンドポイントなので、待ち合わせに使えます。なおこの待ち時間にも GPU の時間課金は走っています。

Q2. 分割された GGUF が 15 個あります。--model には何を渡しますか?

先頭の 1 本(...-00001-of-00015.gguf)だけです。llama.cpp は分割 GGUF をマージせずに直接ロードできます。これが vast.ai の公式ドキュメントが「この種のモデルには llama.cpp が推奨手段」と書く理由です。結合用の作業ディスクも要りません。

Q3. 何も指定せずに起動したら動いたのに、コンテキストが思ったより短くなっていました。誰がやりましたか?

--fit(既定 on)です。「指定しなかった引数を、デバイスメモリに収まるよう調整する」機能なので、-c を書かなければ llama.cpp 側が下げます(コンテキストが対象に入ることは、-fitc が「--fit が設定しうる最小 ctx サイズ」と定義されていることから分かります。調整される引数の完全な一覧は公式ドキュメントに書かれていません)。下限は -fitc の既定 4096、空けておく余白は -fitt の既定 1024 MiB。自分で明示した引数は調整対象になりません。

Q4. RTX 5090 で自分でビルドするとき、CMAKE_CUDA_ARCHITECTURES に何を渡しますか。その根拠は?

"120" です。根拠は 2 つの一次情報の突き合わせで、① llama.cpp 公式が載せている例(-DCMAKE_CUDA_ARCHITECTURES="86;89")から読み取れる変換規則(Compute Capability 8.9 → 89 のように小数点を取る)、② NVIDIA の一覧表が GeForce RTX 5090 を Compute Capability 12.0 に分類していること。裏取りとして、vast.ai の offer が返す 5090 の compute_cap は実測で全件 1200 でした(2026-08-24 時点)。llama.cpp のドキュメントに "120" という例そのものは載っていません。

Q5. Open WebUI に接続を追加したのに、モデルセレクタに何も出ません。確認する順番は?

① URL の末尾に /v1 が付いているか(公式が !! 付きで強調する最頻の失敗)。② ポートが 20000 になっているか(vast.ai の Open WebUI テンプレートは http://localhost:20000 を見に行くよう事前設定されている)。③ ページをリロードしたか。④ Open WebUI が Docker の中なら localhost ではなく host.docker.internal か。⑤ /health が 200 になっているか。接続の検証が赤くても会話は動くことがあるので、検証結果だけで諦めないでください。

まとめ

次章では、ここまでに立てた API を手元の Mac / PC から日常的に使う形に落とします。SSH トンネル・Open WebUI・API クライアントの設定を通して、公開ポートを開けずに自分だけで使う経路を作ります。