# Deploy a large MoE on DGX Spark with vLLM: single node and a two-node pair

> The deployment order that avoids the silent hangs we hit on our own GB10 pair: weights and image pinned, head before peer, readiness by endpoint not by feel, and the kernel-selection check that separates the fast configuration from the one most people run.

- Published: 2026-09-01
- Platform: DGX Spark (GB10)
- Time to complete: 2-3 h
- Canonical: https://agmind.ai/guides/deploy-llm-dgx-spark-vllm/

This is the tutorial version of
[our serving recipe](https://agmind.ai/reports/deepseek-v4-flash-0731-dgx-spark-recipe/) —
the same steps, ordered for someone doing it the first time, with the traps
we paid for marked before you reach them. It was written on a real pair of
GB10 units serving a 284B-parameter MoE; a single Spark follows the same
path minus the fabric section.

You need: one or two DGX Spark units on current firmware, Docker with NVIDIA
runtime, and a lot of disk — frontier-class weights are measured in the
hundreds of gigabytes per node.

## Step 1 — pin everything before you download anything

Three identities decide whether your deployment is debuggable later: the
model revision, the container digest, and the firmware version. Write all
three down first. `main` moves, tags move, and the Spark's firmware updates
[have historically changed behavior enough](https://agmind.ai/essays/benchmark-number-config-in-disguise/)
to flip community sentiment on the whole device. A deployment you cannot
name precisely is a deployment you cannot ask anyone to help debug.

```
# record these three lines somewhere permanent:
# model:    <repo> @ <revision>
# image:    vllm/vllm-openai@sha256:<digest>
# firmware: (from your system info)
```

## Step 2 — weights on every node

For a pair: the full weights go on BOTH nodes, identical revision. Budget
real time for this and verify sizes match when done. For a single node,
pick a model that actually fits one device — the frontier class does not,
which is [why the pair exists](https://agmind.ai/reports/dgx-spark-worth-it/).

## Step 3 — single node first, even if you own two

Bring up one node alone before touching tensor parallelism. You want to
learn the container's behavior — startup time, memory pattern, log shape —
without the fabric as a second variable:

```
docker run -d --name vllm --gpus all \
  -v /var/lib/llm/models:/models -p 8000:8000 \
  vllm/vllm-openai@sha256:YOUR_DIGEST \
  --model /models/YOUR-MODEL --max-model-len 32768
```

Readiness is an endpoint, not a feeling: the server is up when
`curl localhost:8000/v1/models` answers. Weight loading on this class of
model takes minutes — watch `docker logs -f vllm` and do not restart it out
of impatience halfway through.

## Step 4 — for a pair: head first, then peer, in that order

The rank-0 node hosts the coordination store. A peer that starts before the
head is listening dies on a broken pipe with no useful error — this ordering
bug reads obvious written down and
[cost us real hours live](https://agmind.ai/reports/deepseek-v4-flash-0731-dgx-spark-recipe/).
The sequence that works: launch the head, wait until the store port shows in
`ss -tln`, only then launch the peer. Fabric settings (head and peer IPs,
HCA, GID index) belong in environment variables at the top of your launch
script, not scattered through it.

## Step 5 — verify the kernel selection took effect

For MoE models, vLLM's automatic backend selection can deliberately skip the
kernel purpose-built for your model's quantization. Grep the logs for the
MoE backend line and confirm which one loaded — the difference is
[not cosmetic](https://agmind.ai/reports/dsv4-flash-moe-backend-dgx-spark/). If your model
family ships a specialized backend, set it explicitly and check the log
again. In our experience this one check separates a published Spark number from a disappointed forum post.

## Step 6 — the smoke test

Same discipline as [on any box](https://agmind.ai/guides/deploy-llm-strix-halo/): send a real
chat completion, verify `content` is non-empty and `finish_reason` is
`stop`, and take token counts from `usage.completion_tokens` — under
speculative decoding, streaming-event counts lie.

## Step 7 — monitoring, before you need it

Stock GPU observability half-works on GB10 unified memory: dcgm-exporter
does not function and NVML answers N/A on much of what you would alert on.
Set up [the textfile-collector workaround](https://agmind.ai/reports/dgx-spark-gpu-monitoring/)
now — you do not want to discover the observability gap during your first incident.

## Where to go next

- **What to expect from it**: the measured buyer answer, context curve and
  concurrency story are in [the Spark reports](https://agmind.ai/reports/dgx-spark-worth-it/).
- **256K contexts and beyond**: the configuration hunt and the broken-path
  warnings are [their own story](https://agmind.ai/reports/dgx-spark-256k-vllm/).
- **Verify your deployment**: [the harness is public](https://github.com/botAGI/agmind-bench);
  the numbers to compare against live in
  [the benchmark repository](https://github.com/botAGI/dgx-spark-llm-benchmarks).

---

Machine-readable claim registry: https://agmind.ai/claims.json · llms.txt: https://agmind.ai/llms.txt
