SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

VPSでllama.cppサーバーを運用する方法

固定したリリースタグからllama-serverをビルドし、GGUFモデルをOpenAI互換APIで提供します。localhostへの限定、systemd、メモリ制限まで設定します。

構築するもの

VPS で llama.cpp サーバーを実行すると、単一の GGUF モデルファイルを読み込み、OpenAI 互換 API で HTTP リクエストに応答する単一のバイナリ、llama-serverを使用できます。任意の OpenAI クライアントを http://127.0.0.1:8080/v1 に指定すれば動作します。インストールは簡単な部分です。

残りの作業は運用です。バージョンを固定し、ポートを localhost 上だけで待ち受けるようにし、systemd unit を作成し、サーバーのメモリが不足した場合の動作を決めます。このガイドでは、これらを扱います。まだ 2 つの分かりやすい選択肢のどちらにするか決めていない場合は、先にOllama と llama.cpp のトレードオフを読んでください。この比較では意図的に省略している手順を、このガイドで説明します。

リリースタグを選び、記録する

llama.cpp はほぼすべてのマージに対してリリースタグを付けるため、タグはビルド番号として扱われます。b10488 は 18 August 2026 時点で最新です。長期間維持される安定ブランチはありません。そのため、「latest」は変動する対象であり、サポートできるのはテストしたバージョンだけです。タグを1つ選び、記録してください。同じ文字列を clone、バイナリ名、メモで使用します。

各タグにはビルド済みアーカイブも含まれます。CPU のみを使用する x86 VPS では llama-b10488-bin-ubuntu-x64.tar.gz です。x86 ではなく ARM VPS を使用している場合は、その隣に arm64 アーカイブがあります。

curl -LO https://github.com/ggml-org/llama.cpp/releases/download/b10488/llama-b10488-bin-ubuntu-x64.tar.gz
tar tf llama-b10488-bin-ubuntu-x64.tar.gz | head

展開する前にアーカイブの内容を一覧表示し、ファイルの展開先を確認してください。これらのバイナリは、ビルドに使用されたイメージの C ライブラリにリンクされています。そのため、古いディストリビューションでは、インストールされていない GLIBC_ バージョンを示すエラーが起動時に発生します。小規模な VPS でもソースからのビルドには数分しかかからず、この種の問題をすべて回避できます。そのため、以下ではソースからビルドします。

固定したタグから llama-server をビルドする

sudo apt update
sudo apt install -y build-essential cmake git libssl-dev
git clone --depth 1 --branch b10488 https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF
cmake --build build --config Release -t llama-server -j 2

--branch b10488--depth 1 を clone すると、そのタグだけが checkout されます。作業中にビルド内容が変わることはありません。

libssl-dev が必要なのは、LLAMA_OPENSSL オプションがデフォルトで有効になっているためです。このオプションにより、後でバイナリが HTTPS 経由でモデルをダウンロードできます。ヘッダーがないと configure ステップは失敗します。

-DBUILD_SHARED_LIBS=OFF により、自己完結型のバイナリが作成されます。デフォルトのビルドでは共有ライブラリが実行ファイルの隣に配置されるため、実行ファイルだけを /usr/local/bin にコピーすると error while loading shared libraries: libllama.so で失敗します。

-t llama-server は server ターゲットだけをビルドします。デフォルトのビルドでは、ほかのツールとテストもコンパイルされます。2 コアの VPS では、実行しないファイルのために数分余計にかかります。

-j 2 は意図的な設定です。並列コンパイルの各ジョブは独自のワーキングセットを保持します。そのため、小規模プランで -j $(nproc) を実行すると c++: fatal error: Killed signal terminated program cc1plus になります。これは kernel の out-of-memory killer がコンパイラを停止した状態です。ジョブ数を減らすか、ビルド用に swap を追加してください。

変更を検討してもよいフラグが 1 つあります。GGML_NATIVE はデフォルトで有効なため、コンパイラはビルドを実行する CPU に正確に合わせてコードを生成します。実行先のマシン上でビルドする場合は、この設定が適しています。1 回だけビルドして別のホストへバイナリをコピーする場合は、-DGGML_NATIVE=OFF を追加してください。別の CPU にない命令を使用するバイナリは、最初の推論時に Illegal instruction (core dumped) で停止するためです。

タグを含む名前でインストールします。

./build/bin/llama-server --version
sudo install -m 755 build/bin/llama-server /usr/local/bin/llama-server-b10488
sudo ln -sfn /usr/local/bin/llama-server-b10488 /usr/local/bin/llama-server

--version はビルド番号と commit を表示します。checkout したタグと一致している必要があります。一致しない場合は、別のものをビルドしています。ファイル名に番号を含め、そのファイルを symlink から参照する構成にすると、アップグレードは ln -sfn 1 回と再起動 1 回で完了します。ロールバックも、以前の番号を指定して同じコマンドを実行するだけです。

GGUF モデルを取得し、最初にディスク容量を確認する

GGUF は llama.cpp が読み込む単一ファイル形式です。1 つのファイルに重み、tokeniser、メタデータが含まれるため、ほかにインストールするものはありません。ファイル名の suffix は量子化方式を示し、重みを保存する精度を表します。Q4_K_M は 4-bit の混合形式、Q8_0 は 8-bit、f16 は量子化されていない half-precision ファイルです。

ダウンロードする前に、service account とモデル用ディレクトリを作成します。

sudo useradd --system --home /srv/llama --create-home --shell /usr/sbin/nologin llama
sudo install -d -o llama -g llama /srv/models
df -h /srv

サーバーは -hf を使用してモデルを自動取得できます。これは、ビルドが動作することを確認する最も簡単な方法です。

sudo -u llama env LLAMA_CACHE=/srv/models /usr/local/bin/llama-server \
  -hf ggml-org/gemma-3-1b-it-GGUF:Q4_K_M --host 127.0.0.1 --port 8080

LLAMA_CACHE はダウンロード先のディレクトリを指定します。指定しない場合、ファイルはコマンドを実行したアカウントの ~/.cache/llama.cpp に保存されます。これは、これから home directory を読み取り不可にするサービスの保存先として適切ではありません。キャッシュされたファイル名は通常のファイル名ではなく repository name から生成されるため、その後に ls -lh /srv/models を実行します。

サービスで使用する場合は、unit file が安定したパスを参照できるよう、指定したパスへダウンロードします。

sudo -u llama curl -L --output-dir /srv/models -O \
  https://huggingface.co/ggml-org/gemma-3-1b-it-GGUF/resolve/main/gemma-3-1b-it-Q4_K_M.gguf

最初に問題になるのはディスク容量です。以下は、2026 年 8 月 18 日に確認した 2 つのモデルの公開ファイルサイズです。

ChartGGUF file size on disk, published figures, 18 August 2026
The data behind this chart
[
  {
    "label": "gemma-3-1b-it Q4_K_M",
    "size_gb": 0.81
  },
  {
    "label": "gemma-3-1b-it Q8_0",
    "size_gb": 1.07
  },
  {
    "label": "gemma-3-1b-it f16",
    "size_gb": 2.01
  },
  {
    "label": "gpt-oss-20b MXFP4",
    "size_gb": 12.11
  }
]

1B モデルの 4-bit ファイルは 0.81 GB です。同じモデルを量子化しない場合は 2.01 GB になるため、形式の選択だけで容量が 2 倍以上変わります。MXFP4 の 20B モデルは 12.11 GB で、多くのエントリーレベルプランではディスクに収まりません。その後、このファイルをメモリにも読み込む必要があります。特定のモデル系列を検討している場合は、GLM で同じ容量を確認する手順 で、より小さい兄弟モデルなら収まる一方、主要モデルでは VPS の容量上限をすぐに超えることを確認できます。

ダウンロードの前に必ず df -h を確認します。12 GB の転送中に root filesystem が満杯になると、journal を含め、ほかの書き込み処理もすべて失敗します。

手動で 1 回実行し、確認します

sudo -u llama /usr/local/bin/llama-server \
  --model /srv/models/gemma-3-1b-it-Q4_K_M.gguf \
  --host 127.0.0.1 --port 8080 \
  --ctx-size 4096 --parallel 1 --threads 2 --no-webui

別のセッションで、サーバーが準備完了かどうかを確認します。

curl -s http://127.0.0.1:8080/health

ファイルの読み込み中は、HTTP 503 と次の本文が返ります。

{"error":{"code":503,"message":"Loading model","type":"unavailable_error"}}

準備が完了すると、本文は {"status": "ok" } になります。続いて、実際のリクエストを送信します。

curl -s http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"local","messages":[{"role":"user","content":"Say hello in five words."}]}'

choices 配列を含む JSON オブジェクトが返れば、サーバーは稼働しています。model フィールドがあるのは、OpenAI クライアントが常に送信するためです。このサーバーには 1 つのモデルだけが読み込まれているため、この値による選択は行われません。

OpenAI互換APIとポート上で提供されるその他の機能

POST /v1/chat/completionsPOST /v1/completionsPOST /v1/embeddingsはOpenAI互換のルートで、GET /v1/modelsは読み込まれているモデルを返します。GET /healthは前述の準備状態チェックで、GET /propsはサーバーの現在の設定を返します。GET /metricsは、--metricsを指定して起動した場合にPrometheusのカウンターを公開します。

base URLをhttp://127.0.0.1:8080/v1に設定し、空でないAPI key文字列を渡せば、任意のOpenAI SDKを使用できます。--api-keyを自分で設定するまで、そのkeyは検証されません。

他者が示すスループットを、自分の計画にそのまま適用しないでください。CPU推論の速度は、コア数、メモリ帯域幅、同じホストを共有する他の利用者に左右されます。そのため、自分のマシンで1秒あたりのトークン数を測定し、その結果を実際の値として扱ってください。負荷の高い同居利用者によるsteal timeは、時間帯によって生成速度が変動する形で現れます。

127.0.0.1 で待ち受けさせ、前段にプロキシを置く

--host はデフォルトで 127.0.0.1 になっているため、変更するまでサーバーは外部から到達できません。そのままにしてください。llama-server にはユーザーモデル、レート制限、有用な監査ログがありません。唯一の組み込み制御である --api-key も、1 つの文字列を比較するだけです。推論ポートを公開すると、発見した相手に計算資源を無償で使われます。Ollama で同じ設定ミスをした場合も構造は同じです。自己ホスト型モデル API のアクセスを制限する で説明した対策を、ここでも同じように適用してください。

nginx で TLS(transport layer security)を終端し、loopback ポートへプロキシします。

server {
    listen 443 ssl;
    server_name llm.example.com;

    location /v1/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_read_timeout 600s;
    }
}

ストリーミングには proxy_buffering off が必要です。バッファリングを有効にすると、nginx は server-sent events(SSE)をレスポンスが完了するまで保持します。そのためクライアントは無応答のまま待機し、最後に回答全体を一度に受信します。長時間の生成には proxy_read_timeout 600s が必要です。デフォルト値の 60 秒では、遅い回答が 504 Gateway Time-out になるためです。nginx で Certbot と Let's Encrypt を使う で証明書を取得してください。

systemd unit

/etc/systemd/system/llama-server.serviceを記述します。

[Unit]
Description=llama.cpp server
After=network-online.target
Wants=network-online.target

[Service]
User=llama
Group=llama
Environment=LLAMA_ARG_MODEL=/srv/models/gemma-3-1b-it-Q4_K_M.gguf
Environment=LLAMA_ARG_HOST=127.0.0.1
Environment=LLAMA_ARG_PORT=8080
Environment=LLAMA_ARG_CTX_SIZE=4096
Environment=LLAMA_ARG_N_PARALLEL=1
Environment=LLAMA_ARG_THREADS=2
ExecStart=/usr/local/bin/llama-server --no-webui
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
MemoryHigh=3G
MemoryMax=3500M
OOMPolicy=stop
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes

[Install]
WantedBy=multi-user.target

設定はEnvironment=の行に記述します。llama-serverは多くのフラグについてLLAMA_ARG_*変数を読み込み、コマンドライン引数が対応する変数を上書きするためです。これにより、コンテキストサイズを変更する場所を1か所にでき、ExecStartも一目で読める長さに保てます。

ProtectSystem=strictは、このunitに対してファイルシステム全体を読み取り専用にします。サーバーはモデルを読み取るだけなので問題ありません。サービス自体に-hfでモデルをダウンロードさせる場合は、ReadWritePaths=/srv/modelsを追加します。ProtectHome=yes/home/rootを隠します。これが、モデルを/srvに保持する2つ目の理由です。ProtectHomeを有効にすると、デフォルトの~/.cache/llama.cppパスはプロセスからまったく見えなくなります。

sudo systemctl daemon-reload
sudo systemctl enable --now llama-server
systemctl status llama-server
curl -s http://127.0.0.1:8080/health
journalctl -u llama-server -n 50 --no-pager

enable --nowは省略されがちな後半部分です。enableがないと、次回の再起動後にサーバーが起動しません。毎晩の新しいリリースの確認など、サービスに関連する定期処理を実行する場合は、systemd service と timerを使用します。

OOM 発生前に挙動を決める

メモリ使用量には 2 つの部分があり、制限下では異なる動作をします。モデルファイルはデフォルトで memory-mapped されるため、そのページは file-backed です。kernel はページを破棄し、必要になったらディスクから再度読み込めます。KV cache は、アクティブな各会話についてサーバーが保持するトークン単位の状態で、anonymous memory です。これは破棄できないため、プロセスが kill される原因になります。

そのため、unit 内の 2 つの制限は異なる役割を持ちます。MemoryHigh=3G は soft limit です。これを超えると kernel は cgroup に reclaim pressure をかけるため、memory-mapped されたモデルのページが evict され、次のトークン処理時にディスクから再度読み込まれます。サービスは動作を続けますが、処理速度は低下します。MemoryMax=3500M は hard limit です。これを超えるとプロセスが kill され、そのことが journal に明確に記録されます。

llama-server.service: A process of this unit has been killed by the OOM killer.

--ctx-size は自分で設定してください。デフォルトは 0 です。これはモデルの学習時に使用されたコンテキスト長を意味し、最新の long-context model では起動時に非常に大きな KV cache を割り当てます。その結果、サービスは 1 件もリクエストを処理する前に停止します。--parallel は同じコストを倍増させます。各 slot がそれぞれ独自の会話状態を保持するためです。並行処理が必要だと確認できるまでは 1 にしてください。

Restart=on-failure を設定すると、kill されたサービスが復帰します。起動するたびに kill されると、systemd は再起動を諦め、systemctl statusstart request repeated too quickly を出力します。これは正しい動作です。5 秒ごとに 12 GB のファイルを読み直す再起動ループは、サービス停止よりも悪影響が大きいためです。制限またはコンテキストサイズを修正してから、sudo systemctl reset-failed llama-server で状態を消去してください。

リクエストの処理中に systemctl show llama-server -p MemoryCurrent で実際の数値を確認してください。systemd でプロセスのメモリと CPU を制限する では、これらのディレクティブを詳しく説明しています。

このワークロードでは swap を避けてください。swap に退避されたモデルでは、すべてのトークン処理がランダムオフセットのディスク読み取りになります。モデルファイルを memory mapping する方法なら、同じ効果をより小さい影響で実現できます。kernel が必要なページをファイルから直接読み込むためです。

Ollama のほうが適している場合

これは選択の分かれ目です。設定したフラグ、固定したビルド、選択したファイルを使い、1 つのプロセスだけを実行したい場合は llama-server を選択します。他のプロセスが動作していないため、実行中に内容が変わることもありません。

モデル管理が必要な場合は Ollama を選択します。名前を指定したモデルの取得、複数モデルのディスクへの保持、アイドル状態のモデルのアンロード、再ビルドなしでの単一コマンドによるアップグレードが可能です。これらは、Ollama を使わなければ自分でスクリプト化する作業です。VPS で Ollama を実行する は、同じ作業を別の選択で行うものです。どちらも OpenAI-compatible API を提供するため、どちらの方向に切り替えてもクライアントコードはそのまま利用できます。

固定したビルドをアップグレードする

bNNNNNを移行先のタグに置き換えます。

cd llama.cpp
git fetch --tags
git checkout bNNNNN
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF
cmake --build build --config Release -t llama-server -j 2
sudo install -m 755 build/bin/llama-server /usr/local/bin/llama-server-bNNNNN
sudo ln -sfn /usr/local/bin/llama-server-bNNNNN /usr/local/bin/llama-server
sudo systemctl restart llama-server

古いバイナリはディスク上に残るため、ロールバックはllama-server-b10488へ戻すln -sfnと再起動1回で実行できます。移行する前にリリースノートを確認してください。GGUFファイルにはバージョンが付いており、古いファイルも引き続き読み込めますが、フラグ名は変更されることがあります。--mlock--no-mmapはすでに非推奨で、--load-modeの使用が推奨されています。削除されたフラグを渡す unit file は、認識されない引数のメッセージを出して起動に失敗します。

障害パターンと表示される文字列

error while loading shared libraries: libllama.so: バイナリを別の場所にコピーした後に表示されます。デフォルトのビルドでは、バイナリと同じ場所に共有ライブラリも生成されます。-DBUILD_SHARED_LIBS=OFF を指定して再ビルドするか、build/bin ディレクトリ全体をコピーしてください。

Illegal instruction (core dumped): 起動時または最初のリクエスト時に表示されます。バイナリは GGML_NATIVE を有効にして、実行時とは異なる CPU 向けにコンパイルされています。このマシン上で再ビルドするか、-DGGML_NATIVE=OFF を指定して構成してください。

c++: fatal error: Killed signal terminated program cc1plus: ビルド中に表示されます。メモリを使いすぎたため、コンパイラーが強制終了されました。-j を下げるか、ビルド用に swap を追加し、完了後に削除してください。

curl: (7) Failed to connect ... Connection refused: ラップトップから接続したときに表示されます。これは正しい動作です。サーバーは VPS の loopback アドレスで待ち受けています。VPS 自身でテストするか、ssh -L 8080:127.0.0.1:8080 user@your-vps でトンネルを開き、ローカルでは http://127.0.0.1:8080 を使用してください。

再起動後の最初の数秒または数分間に、"message":"Loading model" を伴う HTTP 503 が返されます。数 GB のファイルの読み込みには時間がかかります。また、systemd はプロセスが起動した時点で unit を active と報告するため、モデルがメモリに読み込まれるよりかなり前に起動完了として扱われます。

リクエストがハングした後、504 Gateway Time-out を返します。プロキシがモデルの処理完了を待たずにタイムアウトしています。proxy_read_timeout を増やし、生成されたトークンがそのままクライアントへ届くように proxy_buffering を無効にしてください。

start request repeated too quickly とともに unit が起動と停止を繰り返し、その後停止します。起動するたびに何かがプロセスを終了させています。journalctl -u llama-server で OOM killer の行を確認し、--ctx-size を下げるか、--parallel を下げるか、MemoryMax を増やしてください。

FAQ

llama.cpp の server と Ollama のどちらを VPS で実行すべきですか?

正確なビルドを固定し、正確なフラグを渡し、背後で何も更新されない 1 つのファイルに 1 つのモデルを保持したい場合は、llama-server を実行します。モデル管理、ワンコマンドでのアップグレード、名前によるモデルの取得、複数モデルのディスク上での保持、アイドル状態のモデルのアンロードが必要な場合は Ollama を実行します。これらは自分でスクリプト化する必要がある作業です。どちらも OpenAI 互換 API を提供するため、後で切り替えてもクライアントコードは変わりません。

どの llama.cpp バージョンを固定すべきですか?

実際にビルドしてテストしたタグを使用してください。llama.cpp はほぼすべてのマージにタグを付けます。タグ名は b10488 のようなビルド番号で、これは 18 August 2026 時点で最新でした。独立した安定ブランチはないため、「current」は 1 日に数回変わります。--branch <tag> でクローンし、タグを含むファイル名でバイナリをインストールしてから、シンボリックリンクをそのバイナリに向けてください。これにより、アップグレードとロールバックをそれぞれ 1 コマンドで実行できます。

llama-server にはどの程度の RAM が必要ですか?

まず GGUF ファイルのサイズを基準にします。次に、--ctx-size--parallel スロット数に応じて増加する KV cache を加えます。公開されている数値だけでは、自分の構成に必要な量は判断できません。合計値はモデル、量子化方式、許可するコンテキスト長によって変わるためです。リクエストの処理中に systemctl show llama-server -p MemoryCurrent を実行し、表示された数値を使用してください。

/health が「Loading model」とともに 503 を返すのはなぜですか?

プロセスは起動していますが、モデルファイルがまだメモリに読み込まれていないため、server は {"error":{"code":503,"message":"Loading model","type":"unavailable_error"}} を返します。これは再起動のたびに発生する正常な動作で、ファイルの読み込みに必要な時間だけ続きます。クライアントやプロキシが最初の 503 を致命的な障害として扱う場合にだけ問題になります。/health をポーリングし、{"status": "ok" } が返るまで待ってください。

llama-server をインターネットに直接公開できますか?

0.0.0.0 に bind してポートを開放しないでください。アカウント機能、レート制限、監査に適したリクエストログがなく、組み込みのチェックも単一の文字列を比較する --api-key だけです。デフォルトの 127.0.0.1 bind を維持し、TLS を設定した nginx を前段に置いてください。さらに --api-key も設定し、プロキシ設定のミス 1 つでモデルが全員に公開されないようにします。