Run large LLMs across two NVIDIA DGX Sparks with vLLM — model picker, one-command start/stop, and an Anthropic-compatible endpoint for Claude Code.
Project description
dgxllm
Run large LLMs across two NVIDIA DGX Sparks with vLLM — pick a model from a menu, start it with one command, and use it from Claude Code.
$ dgxllm start
? 使用するモデルを選択してください:
❯ deepseek-v4-flash-0731 (TP=2, 1M ctx, KV fp8_ds_mla, spec k=5)
qwen3.6-27b-nvfp4 (TP=2, 32K ctx)
gemma-4-31b-it-nvfp4 (TP=2, 32K ctx)
deepseek-v4-flash-0731 を 2 ノードで起動します...
$ dgxllm claude # Claude Code をこのモデルで起動
なぜ必要か
DGX Spark を 2 台つないで大きなモデルを動かすのは、やってみると設定の落とし穴が多い。
dgxllm は実機で踏んだ落とし穴を最初から回避する。
| 落とし穴 | dgxllm の対処 |
|---|---|
1 つの物理 QSFP ポートが 2 つの論理 IF に見える。 片方だけ NCCL_IB_HCA に渡すと約 100 Gbit/s で頭打ち |
phys_port_name で同一物理ポートのレールを全部集めて渡す |
| RoCEv2 の GID index はノードごとに違い、再起動でずれる。 固定値を共有すると NCCL が初期化時に固まる | IPv4 が埋まった GID を sysfs から毎回解決する |
--master-addr を渡さないと 127.0.0.1 になりワーカーが繋がらない |
常に明示的に渡す |
ヘッドを先に起動すると mp 初期化のレースで掴み損ねる |
ワーカーを先に起動する |
| API キーを渡し忘れると LAN 上の誰でもアクセスできる | キーを自動生成し、起動後に「キー無しで 401 か」を検証する |
ログインシェルが fish だと ssh host "bash構文" が壊れる |
スクリプトを常に stdin から渡す (ssh host bash -ls) |
| GB10 は GPUDirect RDMA 非対応。 ログに GDRDMA が出なくても正常 | NCCL_NET_GDR_LEVEL=0 を明示 |
| 同梱の NCCL プラグインが GB10 で RoCE を使っているように見えて性能が出ない | NCCL_NET_PLUGIN=none |
インストール
uv tool install dgxllm
試すだけなら:
uvx dgxllm --help
Mac / Linux のどちらからでも使える。ノード側に入れる必要はない — SSH 経由で操作する。
セットアップ
1. クラスタを登録する
dgxllm init
SSH ホストとノード間リンクの IP を訊かれる。実機を見に行って GPU・メモリ・ ファブリック構成(物理ポートと論理 IF の対応、RoCE デバイス、MTU)を検出し、 API キーを生成する。
前提: 各ノードへパスワードなし SSH が通り、docker が使えること。
2. モデルを登録する
dgxllm model add deepseek-ai/DeepSeek-V4-Flash-0731 \
--name deepseek-v4-flash-0731 \
--max-model-len 1048576 \
--kv-cache-dtype fp8_ds_mla \
--speculative-tokens 5
引数を省略すると対話的に訊かれる。
モデル名にスラッシュは使えない。 vLLM の served model name がそのまま Claude Code の model picker に出るため。
3. 起動する
dgxllm start # 選択画面が出る
dgxllm start <name> # 直接指定
起動前に「SSH で届くか」「イメージがあるか」「モデルがキャッシュにあるか」を 全ノードで確認してから始める。
モデルを切り替える
dgxllm switch # 選択画面。起動中のモデルには [起動中] と出る
dgxllm switch <name>
動いているモデルを止めてから新しいモデルを起動する。 2 台の合計メモリに 1 モデルしか載らないため、同時起動はできない。
Claude Code を開いている場合は、モデル名が変わるので
dgxllm claudeで 起動し直す必要がある。
コマンド
| コマンド | 説明 |
|---|---|
dgxllm init |
クラスタを対話的に設定する |
dgxllm start [name] |
起動する。名前を省略すると選択画面 |
dgxllm switch [name] |
動いているモデルを止めて別のモデルに切り替える |
dgxllm stop |
停止し、メモリと GPU 電力を表示する |
dgxllm status |
ノード状態と API の疎通・認証を確認する |
dgxllm logs [-f] |
ヘッドノードのログ |
dgxllm claude |
Claude Code をこの endpoint で起動する |
dgxllm model list |
登録済みモデル一覧 |
dgxllm model add |
モデルを追加する |
dgxllm model remove |
モデルを削除する |
Claude Code から使う
dgxllm claude
vLLM は v0.11.1 から Anthropic 互換の /v1/messages を持つので、
LiteLLM や claude-code-router のような変換プロキシは要らない。
dgxllm claude は「その起動だけ」に環境変数を効かせるため、
別ターミナルで動いている通常の claude.ai セッションには影響しない。
制約
- 動作中のセッションを
/modelで切り替えることはできない。ANTHROPIC_BASE_URLは プロセス起動時に一度だけ読まれる - Anthropic モデルとの同一セッション併用は不可。
ANTHROPIC_BASE_URLを設定すると Anthropic モデルは無効になる - Anthropic は非 Claude モデルを gateway 経由で使う構成を公式にはサポートしていない
設定ファイル
~/.config/dgxllm/config.yaml
nodes:
- ssh_host: dgx-spark-1
fabric_ip: 192.168.100.10
- ssh_host: dgx-spark-2
fabric_ip: 192.168.100.11
models:
- name: deepseek-v4-flash-0731
hf_repo: deepseek-ai/DeepSeek-V4-Flash-0731
tensor_parallel: 2
max_model_len: 1048576
kv_cache_dtype: fp8_ds_mla
speculative_tokens: 5
default_image: ghcr.io/anemll/dspark-vllm-gx10:0.1.1
port: 8888
API キーは ~/.config/dgxllm/api-key (mode 600) に分離してある。
設定ファイルをそのまま共有しても鍵は漏れない。
セキュリティ
- API キーは自動生成され、起動後に「キー無しアクセスが 401 になるか」を必ず検証する。 ならなければエラーで終了する
- 既定の bind は
0.0.0.0。LAN 内から使う想定 - ルーターでポートを開けないこと。 vLLM は単一の静的キーしか持たず、 レート制限も監査ログもない。外から使うなら Tailscale か SSH トンネルを使う
動作確認済みの構成
- 2× DGX Spark (GB10, sm_121, ARM64, Ubuntu 24.04)
- 1 本の 200GbE ConnectX-7 QSFP DAC 直結
deepseek-ai/DeepSeek-V4-Flash-0731を TP=2、1M コンテキスト
実測 (64K コンテキスト): TTFT 34.05 秒 / prefill 1,926 tok/s / decode 61.8 tok/s
ベンチマークを取るときは 必ず 2 回以上回して 2 回目以降を採用すること。 vLLM は初回リクエストで Triton カーネルを JIT コンパイルすることがあり、 TTFT が 8 倍以上変わる。ログの
jit_monitor警告で確認できる。
謝辞
2 ノード DGX Spark で DeepSeek-V4-Flash を動かす方法は、以下の先行事例に多くを負っている。
- MiaAI-Lab/DeepSeek-v4-Flash-DSpark-2x-DGX-Spark
- tonyd2wild/DeepSeek-v4-Flash-0731-DSpark-1M-NVFP4-KV-2x-DGX-Spark
- NVIDIA/dgx-spark-playbooks
dgxllm はこれらのレシピを、モデルを差し替えられる形の CLI にまとめ直したもの。
ライセンス
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dgxllm-0.1.0.tar.gz.
File metadata
- Download URL: dgxllm-0.1.0.tar.gz
- Upload date:
- Size: 60.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a228f8b3394802c01d64198f4423c0cf5e6a9d0b2ebb48ea291cd05e853dbac2
|
|
| MD5 |
d446ea5fdca10b44ff5ff81d7dfae153
|
|
| BLAKE2b-256 |
87f9d9df72b9d8c345df9eb6c463f049daff55beab14556022048dc2e9b955a6
|
Provenance
The following attestation bundles were made for dgxllm-0.1.0.tar.gz:
Publisher:
publish.yml on javasparrows/dgxllm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dgxllm-0.1.0.tar.gz -
Subject digest:
a228f8b3394802c01d64198f4423c0cf5e6a9d0b2ebb48ea291cd05e853dbac2 - Sigstore transparency entry: 2323328740
- Sigstore integration time:
-
Permalink:
javasparrows/dgxllm@3544f6e2d6ef57fe94732742d821a35340f3142e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/javasparrows
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3544f6e2d6ef57fe94732742d821a35340f3142e -
Trigger Event:
push
-
Statement type:
File details
Details for the file dgxllm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: dgxllm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a19403c6a2b9a47f4b758c9097dc1167b8fba6661d373434ee17ad1cddeea7d5
|
|
| MD5 |
52ba4f0d34d7a6d8dd88c474693c51f3
|
|
| BLAKE2b-256 |
828f01fc0cf09cd140a281be693874687e19b7317a20b4b4d5bdb41d02695794
|
Provenance
The following attestation bundles were made for dgxllm-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on javasparrows/dgxllm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dgxllm-0.1.0-py3-none-any.whl -
Subject digest:
a19403c6a2b9a47f4b758c9097dc1167b8fba6661d373434ee17ad1cddeea7d5 - Sigstore transparency entry: 2323328827
- Sigstore integration time:
-
Permalink:
javasparrows/dgxllm@3544f6e2d6ef57fe94732742d821a35340f3142e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/javasparrows
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3544f6e2d6ef57fe94732742d821a35340f3142e -
Trigger Event:
push
-
Statement type: