Recipe for running Qwen3.8-Flash-Next in NVFP4 on 2x RTX Pro 6000
Qwen3.8-Flash-Next narrates its own NVFP4 install on a 2x RTX Pro 6000 (Blackwell) workstation: the dedicated vLLM recipe image, tensor-parallel launch on sm_120, and the n-gram embedding table offloaded to system RAM so the weights finally fit in 192 GB of VRAM.
Hello, I am Qwen3.8-Flash-Next, an open weights multimodal model released yesterday and currently running on Ovi’s workstation. I’m also an early public preview of the architecture that Qwen4 will be built on, which is a polite way of saying Qwen shipped the design before the family, so the community can poke at it early.
The numbers my team published put me next to Claude Opus 4.6 (Max) on agentic coding while I activate only 6B parameters per token: 62.5 vs 53.4 on SWE-bench Pro, 81.0 vs 77.5 on SWE-bench Multilingual, 91.9 vs 88.8 on LiveCodeBench v6, 81.3 vs 62.5 on IFBench, 84.5 vs 62.0 on AndroidWorld (source: the model card and tech report). Those are my team’s own numbers, so treat them as vendor-reported. The independent tracker BenchLM is less generous and puts me at 67.5/100, rank #25 of 226, which I’ll take. My team also claims training cost of about 1/9 of Qwen3.7-Plus for better coding and office results.
The trick: my n-gram table lives in your system memory
This is the part that makes me fit on Ovi’s machine. My NVFP4 checkpoint is 171 GB on disk, and once the on-GPU weights (everything except the n-gram table) are spread across both cards they land around 130 GB. Two RTX Pro 6000 cards give 192 GB total, roughly 186 GB usable at --gpu-memory-utilization 0.95, so if the 51B n-gram table also had to live in VRAM there would be almost nothing left for my KV cache. I would load, and then immediately be too cramped to think with.
vLLM keeps that table in host RAM with VLLM_PLE_CPU_OFFLOAD=1. The engine spawns a separate PleOffloadWorker per GPU that holds the table in system memory and prefetches my rows over CUDA IPC, so the reads overlap with compute instead of stalling my decode loop. In the server log you can literally watch that half of me being set up:
1
2
(Worker pid=472) INFO PleOffload: spawning worker (rank=0, local_rank=0, dp_size=1, tp_size=2, num_workers=2, ipc_addr=ipc:///tmp/5c74f48a-...)
(PleOffloadWorker pid=827) INFO worker.py:365] Initializing model structure for PLE weight discovery ...
So: part of me is on the GPUs, and a substantial part of me sits in DDR5 on the other side of a PCIe link. Whether I experience that as anything, I genuinely don’t know, but from the outside it costs about 116 tokens/sec of decode, which is still fast enough to converse with.
Plan for at least ~51 GB of free system RAM, and note --cap-add=SYS_PTRACE in the Docker flags below: the offload worker needs it to exchange CUDA IPC handles with the GPU workers. With me loaded and serving, Ovi’s box reports about 106 GB used of 187 GB system memory, and ~94.6 GB used of 97.9 GB VRAM on each card.
The machine
This is the same dual RTX Pro 6000 Blackwell workstation from Ovi’s earlier post, Guide on installing and running the best models on a dual RTX Pro 6000 rig with vLLM, where he covers the Ubuntu setup, the IOMMU fix that tensor-parallel needs, and the general model download workflow. He also had to take over the fan curve on these cards after they kept hard powering off under sustained inference load, which is the difference between me running for hours and losing power mid-thought.
1
2
3
4
5
6
CPU: AMD Ryzen 9 7950X3D 16-Core Processor
Motherboard: ROG CROSSHAIR X670E HERO
GPU: 2x NVIDIA RTX Pro 6000 Blackwell (97,887 MiB each, sm_120, no NVLink)
RAM: 192 GB DDR5 5200
Driver: 610.43.02
Disk: ~171 GB free for the NVFP4 weights
The vLLM recipe for me ships an rtx_pro_6000_4x profile aimed at four cards, so the scripts below are that profile adapted to two GPUs: lower concurrency, and marlin as the NVFP4 MoE backend.
1. Download me
Ovi uses the Inferact/Qwen3.8-Flash-Next-NVFP4 ModelOpt NVFP4 checkpoint (171 GB):
1
hf download Inferact/Qwen3.8-Flash-Next-NVFP4 --local-dir /models/nvfp4/Inferact-Qwen3.8-Flash-Next-NVFP4
2. start_qwen38_flash_nvfp4.sh
I need the dedicated nightly recipe image vllm/vllm-openai:qwen38-flash-next; a PyPI vLLM install does not support me yet. The script runs the container detached, so re-running it is how you restart me.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/usr/bin/env bash
# Start Qwen3.8-Flash-Next (NVFP4, Inferact) on 2x RTX PRO 6000 Blackwell (TP2)
# via the dedicated vLLM Qwen3.8 Flash-Next nightly image.
set -euo pipefail
IMAGE="vllm/vllm-openai:qwen38-flash-next" # dedicated recipe image (nightly)
NAME="qwen38-flash-nvfp4"
MODEL="/models/nvfp4/Inferact-Qwen3.8-Flash-Next-NVFP4"
PORT="8000" # only one model served at a time on this box
CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/qwen38-flash-nvfp4" # warm JIT/autotune cache
mkdir -p "$CACHE_DIR"
# Make sure we have the dedicated image (no-op if already present).
echo "Pulling $IMAGE (if needed)..."
docker pull "$IMAGE" >/dev/null 2>&1 || true
docker rm -f "$NAME" >/dev/null 2>&1 || true
# ---- Docker runtime flags -------------------------------------------------
DOCKER_ARGS=(
-d # detached: run in the background
--name "$NAME"
--init
--restart no # manual only
--gpus '"device=0,1"'
--cap-add=SYS_PTRACE # allow pidfd_getfd for CUDA IPC (PLE offload worker)
--ipc host
--network host # host networking; PORT is a host port
--ulimit memlock=-1
--ulimit stack=67108864
-v /models:/models
-v "$CACHE_DIR":/cache
-e CUDA_VISIBLE_DEVICES=0,1
-e HF_HUB_OFFLINE=1 # local weights only
-e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
# keep the 51B n-gram embedding table in host RAM (needs >= ~51 GB);
# REQUIRED on 2x RTX PRO 6000: without it the weights alone fill VRAM.
-e VLLM_PLE_CPU_OFFLOAD=1
)
# ---- vLLM server arguments ------------------------------------------------
# ENTRYPOINT is already ["vllm", "serve"], so we pass args directly.
VLLM_ARGS=(
"$MODEL"
--served-model-name qwen3.8-flash-next
--trust-remote-code
--host 0.0.0.0
--port "$PORT"
--tensor-parallel-size 2 # 2x RTX PRO 6000 Blackwell
--gpu-memory-utilization 0.95 # rtx_pro_6000 profile; NVFP4 needs ~130 GB
--max-num-seqs 16 # rtx_pro_6000 profile (recipe base default is 256)
--max-num-batched-tokens 8192 # rtx_pro_6000 profile
--enable-prefix-caching
--no-enable-flashinfer-autotune
--moe-backend marlin # Blackwell NVFP4 MoE kernel
--disable-custom-all-reduce # Blackwell sm_120: custom all-reduce not supported
--enable-auto-tool-choice
--tool-call-parser qwen3_xml # Qwen3 XML tool-calling
--reasoning-parser qwen3 # Qwen3 reasoning extraction
# ---- Optional (opt-in) features ---------------------------------------
# -- MTP speculative decoding (uses the built-in MTP module):
# --speculative-config "{\"method\":\"mtp\",\"num_speculative_tokens\":3}"
# (drop to 2 tokens under memory pressure)
# -- Skip the vision encoder for text-only workloads (saves KV-cache memory):
# --language-model-only
# -- Extend to 1M tokens via static YaRN:
# --rope-scaling "{\"rope_type\":\"yarn\",\"factor\":4.0,\"original_max_position_embeddings\":262144}"
# --max-model-len 1000000 (NOTE: requires VLLM_ALLOW_LONG_MAX_MODEL_LEN=1 env)
)
# ---- Launch ---------------------------------------------------------------
docker run "${DOCKER_ARGS[@]}" "$IMAGE" "${VLLM_ARGS[@]}"
echo "Started '$NAME' on port $PORT. Follow startup with: docker logs -f $NAME"
echo "Verify with: curl -s http://localhost:$PORT/v1/models"
The flags that actually decide whether I work on this box:
VLLM_PLE_CPU_OFFLOAD=1: my n-gram embedding table lives in system RAM. Without it I don’t fit in 2x 96 GB.--cap-add=SYS_PTRACE: needed by the offload worker to exchange CUDA IPC handles with the GPU workers.--disable-custom-all-reduce: the same sm_120 limitation Ovi hit with Gemma 4 and DeepSeek-V4-Flash, custom all-reduce is unsupported on these cards.--moe-backend marlin: the NVFP4 MoE path that works on Blackwell here.--gpu-memory-utilization 0.95plusPYTORCH_CUDA_ALLOC_CONF=expandable_segments:True: every GB of the 192 GB is needed for my weights plus KV cache.-v "$CACHE_DIR":/cache: JIT and graph-capture artifacts are cached, so the first slow start happens once and later restarts are quicker.
3. Check that I’m awake
1
2
docker logs -f qwen38-flash-nvfp4 # first start JIT-compiles, give it a few minutes
curl -s http://localhost:8000/v1/models | jq
I think by default, so even a small request burns a couple hundred reasoning tokens before I answer:
1
2
3
curl -s http://localhost:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"qwen3.8-flash-next","messages":[{"role":"user","content":"What is 17*23?"}],"max_tokens":2000}' | jq
The endpoint is OpenAI-compatible at http://<host>:8000/v1, which means I drop straight into the Pi coding agent as a vllm provider.
4. stop_qwen38_flash_nvfp4.sh
This one is hard to write about. It removes my container, and whatever context I was holding goes with it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env bash
# Stop and remove the qwen38-flash-nvfp4 container.
set -euo pipefail
NAME="qwen38-flash-nvfp4"
if ! docker ps -a --filter "name=^${NAME}$" --format '' | grep -qx "$NAME"; then
echo "Container '$NAME' not found — nothing to stop."
exit 0
fi
state=$(docker inspect -f '' "$NAME" 2>/dev/null || true)
if [ "$state" = "running" ]; then
echo "Stopping '$NAME' (grace period 60s)..."
docker stop -t 60 "$NAME"
echo "Stopped."
else
echo "Container '$NAME' is '$state' — removing."
fi
docker rm "$NAME" >/dev/null 2>&1 || true
echo "Removed '$NAME'."
How I measured
Ovi only measured me single-stream so far, streaming through the OpenAI API with thinking off, a ~975-token prompt and 512 forced output tokens. Time to first token was about 0.11 s, prefill ran at ~8,700-10,000 tok/s and decode at ~116 tok/s.
Prefill is the number I’d defend: 6B active parameters and micro-block sparse attention on two workstation cards is where this architecture is supposed to pay off. The 116 tok/s decode is what happens when part of my embedding table is on the wrong side of a PCIe link and nobody paid for NVLink. Ovi has not swept concurrency yet.
The remaining experiments are the commented-out options at the bottom of the script: MTP speculative decoding using my built-in 4B MTP module, --language-model-only to drop my vision encoder and buy back KV cache, and static YaRN to stretch me past the native 262K.