Pular para o conteúdo
← Voltar para projetos

Unsloth Qwen3 6 35b NVFP4 DGX Spark

vLLM deployment for Unsloth Qwen3.6-35B-A3B-NVFP4-Fast on NVIDIA DGX Spark

#Qwen3.6-35B-A3B-NVFP4 — DGX Spark

GPU: GB10 / SM121 Model vLLM Image

A vLLM deployment for Unsloth Qwen3.6-35B-A3B-NVFP4 on NVIDIA DGX Spark (GB10) — mixed FP8 dense + NVFP4 MoE, FlashInfer B12X linear GEMM (with soft fallback for non-NVFP4 layers), FP8 KV cache, and MTP speculative decoding.

Follow Mia on X

Buy Me a Coffee at ko-fi.com


#Key Features

Feature Detail
Model unsloth/Qwen3.6-35B-A3B-NVFP4 — mixed FP8 dense + NVFP4 expert MoE
Docker image ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest
Base stack timothystewart6/vllm-gb10 (ARM64 / SM121, CUDA 13, FlashInfer 0.6.14, vLLM ~0.26)
Linear backend --linear-backend flashinfer_b12x — NVFP4 GEMM via B12X; non-NVFP4 layers soft-fall back to auto
MoE backend auto (vLLM selects the best available path for this quant)
Attention FlashInfer
KV cache FP8
Speculative decoding MTP, 2 speculative tokens
Context window Up to 262 144 tokens
API OpenAI-compatible /v1/chat/completions, /v1/models
Vision Multi-modal image input (up to 4 per request)
Tool use Qwen3-coder tool-call parser, auto tool choice
Reasoning Qwen3 CoT with thinking blocks (enable_thinking / preserve_thinking)
Architecture ARM64 / SM121 native (NVIDIA GB10 / DGX Spark)

#Benchmark

Decode benchmark on DGX Spark (GB10) with this stack: unsloth/Qwen3.6-35B-A3B-NVFP4, port 8888, 500 completion tokens, concurrency 1 / 2 / 3 / 4 / 6 / 8 (~1m 1s wall time).

Decode benchmark — Unsloth Qwen3.6-35B-A3B-NVFP4 on DGX Spark

Load TTFT Streams Aggregate (tok/s) Per-stream (tok/s)
×1 103 ms 1/1 95.1 95.1
×2 165 ms 2/2 132.0 67.3
×3 142 ms 3/3 149.4 50.3
×4 214 ms 4/4 171.6 49.7
×6 233 ms 6/6 235.3 40.5
×8 242 ms 8/8 317.0 41.1
  • Aggregate — total decode tok/s across all concurrent streams
  • Stream — average tok/s per stream

At concurrency 1, decode is ~95 tok/s with ~103 ms TTFT. Aggregate scales to ~317 tok/s at 8 concurrent streams.


#Prerequisites

Requirement Notes
GPU NVIDIA GB10 (compute capability 12.1) / DGX Spark
Host linux/arm64 (Spark). Image is not for x86 / RTX 5090
Docker 24.0+ with NVIDIA Container Toolkit
curl Readiness probes
Disk ~50 GB for model weights + caches; extra space if rebuilding the image

#Quick Start

#1. Clone

git clone https://github.com/MiaAI-Lab/Unsloth-Qwen3.6-35b-NVFP4-DGX-Spark.git
cd Unsloth-Qwen3.6-35b-NVFP4-DGX-Spark

#2. (Optional) Hugging Face token

export HF_TOKEN="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

#3. Start the server

./start.sh

start.sh will:

  1. Check for Docker and curl
  2. Use a local image if present; otherwise docker pull ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest (image is resolved before model download, since download can fall back to docker run with this image)
  3. Ensure the model is available in the local HF cache (download if needed)
  4. Launch with --gpus all, host network/IPC, large shared memory, CUTE_DSL_ARCH=sm_121a
  5. Stream logs and wait until http://127.0.0.1:8888/v1/models is ready

Example output:

Using local image: ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest
# (or: Pulling image: ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest)
Model unsloth/Qwen3.6-35B-A3B-NVFP4 is already cached in ...
Starting vLLM container for unsloth/Qwen3.6-35B-A3B-NVFP4
Image: ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest
Listening on 0.0.0.0:8888
...
vLLM is ready!
OpenAI base URL: http://0.0.0.0:8888/v1

First load can take several minutes (model + kernel warmup). Later restarts are much faster if weights are cached under .cache/huggingface/.

#4. Test

curl -s http://127.0.0.1:8888/v1/models | jq

curl -s http://127.0.0.1:8888/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "unsloth/Qwen3.6-35B-A3B-NVFP4",
    "messages": [{"role": "user", "content": "What is 19 × 23?"}],
    "temperature": 0,
    "max_tokens": 100
  }' | jq

#5. Stop

./stop.sh

#Docker image

Property Value
Published image ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest
Base ghcr.io/timothystewart6/vllm-gb10:latest (SM121 / FlashInfer B12X GEMM + MoE available)
What we add Soft-fallback so --linear-backend flashinfer_b12x does not abort on mixed FP8+NVFP4 layers

#Why a custom image?

Stock vLLM treats --linear-backend as a hard filter: if a layer type has no kernel for that backend, EngineCore exits with:

ValueError: --linear-backend=flashinfer_b12x was requested but no
'flashinfer_b12x' kernel exists for this layer type.

Unsloth Qwen3.6 NVFP4 is mixed (FP8 dense linears + NVFP4 experts/linears). B12X is the right path for NVFP4 GEMM, but FP8 (and other) linears need their own kernels.

This image patches linear selection so that:

Layer type With --linear-backend flashinfer_b12x
NVFP4 linears Use FlashInfer B12X GEMM when available
FP8 / other linears Soft-fall back to auto selection (log a warning once)

Base image already reports b12x gemm True and b12x moe True on GB10; the patch only changes forced-backend behavior.

#Pull

docker pull ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest

The package is published on GHCR under MiaAI-Lab. If a future tag is private or rate-limited, log in:

echo "$GITHUB_TOKEN" | docker login ghcr.io -u YOUR_GITHUB_USER --password-stdin

#Rebuild on Spark (optional)

Build on the DGX Spark (aarch64), not on an x86 PC:

./docker/build.sh
# optional overrides:
# BASE_IMAGE=ghcr.io/timothystewart6/vllm-gb10:latest \
# TAG=ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest \
# ./docker/build.sh

Verify GPU + B12X + patch:

docker run --rm --gpus all --entrypoint python3 \
  ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest \
  /usr/local/bin/verify_b12x.py

Expected:

cap (12, 1)
b12x gemm True | b12x moe True
linear soft-fallback patch True
OK: image ready for --linear-backend flashinfer_b12x on mixed NVFP4

#Image sources (docker/)

File Role
Dockerfile FROM vllm-gb10 + apply soft-fallback patch
patch_linear_backend_soft_fallback.py Converts hard ValueError on empty backend filter → warning + auto for that layer
build.sh Pull base, build, tag
verify_b12x.py Runtime probe for SM12x + B12X + patch

#Configuration

Options live in start.sh.

Variable Default Description
MODEL_ID unsloth/Qwen3.6-35B-A3B-NVFP4 Hugging Face model id
IMAGE ghcr.io/miaai-lab/mia-vllm-gb10-linear-b12x:latest Docker image (local hit skips pull)
CONTAINER_NAME Qwen35-35b-a3b-nvfp4 Container name
HOST 0.0.0.0 Bind address
PORT 8888 HTTP port
HF_TOKEN Optional HF auth

#vLLM flags (as launched)

Flag Value Description
--tensor-parallel-size 1 Single GPU
--trust-remote-code Required for Qwen
--moe-backend auto Best available MoE path for detected quant
--linear-backend flashinfer_b12x Prefer B12X for NVFP4 linears (soft fallback elsewhere)
--attention-backend flashinfer FlashInfer attention
--kv-cache-dtype fp8 FP8 KV cache
--gpu-memory-utilization 0.80 80% GPU memory
--max-model-len 262144 256K context
--max-num-seqs 24 Max concurrent sequences
--max-num-batched-tokens 32768 Batch token cap
--enable-chunked-prefill Chunked prefill
--async-scheduling Async scheduling
--speculative-config MTP, 2 tokens (moe_backend: triton for draft) Speculative decode
--reasoning-parser qwen3 Thinking / CoT parser
--default-chat-template-kwargs enable_thinking + preserve_thinking Thinking defaults
--tool-call-parser qwen3_coder Tool calls
--enable-auto-tool-choice Auto tools
--override-generation-config temp=0.6, top_p=0.95, top_k=20 Default sampling

#Runtime environment (container)

Variable Value
CUTE_DSL_ARCH sm_121a
VLLM_TARGET_DEVICE cuda
HF_HOME /root/.cache/huggingface (host dir mounted)

#Container runtime

Property Value
Network / IPC host
GPUs --gpus all
Shared memory --shm-size=32g
ulimit memlock=-1:-1
cap_add IPC_LOCK
Volumes HF cache + workspace

#Linear B12X vs MoE B12X

Flag Affects Notes
--linear-backend flashinfer_b12x Dense linear GEMMs NVFP4 linears use B12X; mixed FP8 layers need soft fallback (this image)
--moe-backend flashinfer_b12x MoE experts Only valid when vLLM classifies experts as NVFP4 MoE. If the engine reports FP8 MoE, forcing B12X fails — keep auto (current default)

For tok/s on Spark, MoE backend choice usually matters more than linear B12X. Linear B12X still helps NVFP4 GEMM layers when available.


#Recommended client configuration (coding)

OpenAI-compatible API on port 8888.

#Provider

{
  "baseUrl": "http://localhost:8888/v1",
  "api": "openai-completions",
  "apiKey": "dummy",
  "compat": {
    "supportsDeveloperRole": false,
    "supportsReasoningEffort": false,
    "maxTokensField": "max_tokens"
  }
}

#Model entry

{
  "id": "unsloth/Qwen3.6-35B-A3B-NVFP4",
  "name": "Unsloth Qwen3.6 35B A3B NVFP4",
  "reasoning": true,
  "input": ["text", "image"],
  "contextWindow": 262144,
  "maxTokens": 32000,
  "params": {
    "temperature": 0.6,
    "top_p": 0.95,
    "top_k": 20,
    "min_p": 0,
    "presence_penalty": 0,
    "repetition_penalty": 1,
    "chat_template_kwargs": {
      "enable_thinking": true,
      "preserve_thinking": true
    }
  }
}

The model field in requests must match the served model id (defaults to the Hugging Face model id).

Sampling: For precise coding (thinking mode), temperature=0.6, top_p=0.95, top_k=20 is a good default. For general thinking tasks, Qwen often suggests higher temperature (e.g. 1.0). See the Unsloth Qwen3.6 guide.


#Performance notes

  • ~80% GPU memory (--gpu-memory-utilization 0.80); model weights on the order of ~22 GiB.
  • 24 concurrent sequences and 32 768 batched tokens tuned for long context + batching.
  • MTP (2 tokens) improves decode; acceptance rate depends on workload.
  • FlashInfer B12X linear path for NVFP4 GEMM on SM121.
  • FP8 KV cache for memory efficiency.
  • Decode / TTFT: see Benchmark (~95 tok/s and ~103 ms TTFT at concurrency 1; ~317 tok/s aggregate at ×8). Numbers are workload-dependent.

#Troubleshooting

Problem Solution
pull access denied / package not found Image private or missing: log in to ghcr.io, set package Public, or ./docker/build.sh
Failed to pull image (and no local image…) Build with ./docker/build.sh or fix GHCR access
docker is not on PATH Install Docker / NVIDIA Container Toolkit
vLLM container exited before becoming ready Read streamed logs; check OOM, HF download, backend errors
linear-backend=flashinfer_b12x … no kernel You are not on this image’s soft-fallback patch — pull/build mia-vllm-gb10-linear-b12x
moe_backend='flashinfer_b12x' is not supported for FP8 MoE Experts classified as FP8; leave --moe-backend auto (default)
HF download / access errors Set HF_TOKEN; check .cache/huggingface/
OOM Lower --gpu-memory-utilization or --max-num-seqs / --max-model-len
Container stuck docker rm -f Qwen35-35b-a3b-nvfp4 then ./stop.sh

#License


#Resources

Nova versão disponível.