Memory budget: what one start must fit

Result, 2026-10-24. DeepSeek-V4-Flash-0731 NVFP4, TP=2 on 2x DGX Spark (GB10): one rank holds 102.0 GiB (117.2 GiB available before the start, 15.2 after). With ~114.5 GiB free at Start, the largest KV pin the start gate passes is 14.8e9 B without the drafter, 9.4e9 B with it (computed below).

What one rank holds

TermGiB a rankSource
Weights79.29vLLM “Model loading took 79.29 GiB”, generation 235 (shard headers said 76.68)
Drafter (DSpark, mtp.*)5.06shard headers, 10.12 ÷ 2; not measured, the drafter has not loaded yet
KV pool, --kv-cache-memory 10e99.3110 × 10⁹ B ÷ 2³⁰; backs 181,777 tokens
Overhead13.4102.0 − 79.29 − 9.31: CUDA graphs up to batch 48, kernel workspaces, activations at an 8,192-token prefill budget
Held, no drafter102.0117.2 − 15.2 GiB available, head box, generation 235
Start headroom8.0the load balancer’s start gate

Overhead is a remainder, not a measurement of each part. Before this start the row carried 4.3 GiB of overhead copied from another model; the real figure is 3.1x that.

The start gate

Per box, before a launch:

total = weights + drafter (if speculative) + KV pin + overhead
        + prefill-budget term + seat term     # both 0 here: 8,192 and 8 seats are inside the calibration
needs = total + 8 GiB
start if needs ≤ MemAvailable on every box

The load balancer's memory estimate; one formula, every constant read from the model's catalog row.

Memory one DeepSeek-V4-Flash rank needs to start, GiB: without drafter at KV 8, 10, 16, 20e9 B: 108.1, 110.0, 115.6, 119.3; with drafter: 113.2, 115.1, 120.7, 124.4; free at Start about 114.5 Needed to start, GiB a rank (3.6 px per GiB) weights 79.29 drafter 5.06 KV pin overhead 13.4 headroom 8 KV 8e9 108.1 KV 10e9 (serving) 110.0 KV 16e9 115.6 ✕ KV 20e9 119.3 ✕ drafter, KV 8e9 113.2 drafter, KV 10e9 115.1 ✕ drafter, KV 16e9 120.7 ✕ drafter, KV 20e9 124.4 ✕ free at Start ~114.5 Bars start at 0 GiB. A bar whose dashed headroom crosses the line is refused (✕).
Computed with the gate's formula from the measured row (79.29 / 5.06 / 13.4 GiB). Free figure: head box, 2026-10-24.
KV pinKV GiBneeds, no drafterneeds, draftervs ~114.5 free
8e97.45108.1113.2both start
10e99.31110.0115.1drafter refused
16e914.90115.6120.7both refused
20e918.63119.3124.4both refused

The 20e9 + drafter row is a start the operator asked for on 2026-10-24 and the gate refused: 124.4 GiB needed, ~114.5 free. The largest pins that pass: 114.5 − 8 − 79.29 − 13.4 = 13.81 GiB = 14.8e9 B without the drafter, 8.75 GiB = 9.4e9 B with it.

Why 8 GiB, twice

Two different 8 GiB checks, at two different moments:

Start gateMemory guard
Runsbefore the launch, in the load balancerduring the load, on each box
Readsthe estimatethe box’s MemAvailable, every 2 s
Actsrefuses the startkills the rank after 3 polls in a row under 8 GiB
Stops at—the head serving; a loaded pair runs below 8 GiB by design

The headroom is 8 GiB because of 2026-09-30: a prefill budget of 16,384 left the pair at 0.8–4.2 GiB available, the kernel log filled with NVRM NV_ERR_NO_MEMORY, and both boxes hung. The configuration that had served for weeks left 9.1 GiB. 8 sits under the second and well above the first.

Generation 234 shows why the guard is needed after the gate. It started at a 16e9 pin while the row still carried the 4.3 GiB overhead guess: 76.68 + 14.90 + 4.3 + 8 = 103.9 GiB needed against ~117 free, so the gate passed it (computed from the pre-measurement row). The real overhead was 13.4 GiB. During kernel warmup the box fell to 7.9 GiB available and the guard killed the start. The next start, at 10e9, is the one serving.

GB10: one memory for the OS and the GPU

What to change when a start does not fit: KV pool (largest movable term), KV type, drafters (5.06 GiB here), prefill budget and concurrency (charged above the calibrated 8,192 tokens and 16 seats).

Part of vLLM settings, explained.

Questions? Let's Chat :)

vllm settings