dgx-spark-qwen38 README

The project's README from github.com/hasso5703/dgx-spark-qwen38. As one markdown file: README.md.

Qwen3.8 on DGX Spark (GB10): 27B at 50 tok/s, Flash-Next 176B on one box

One command installs a boot-persistent, hardened serving stack for the Qwen3.8 family on a single DGX Spark, with seven switchable targets and zero quality loss on each (NVFP4 is the quantization floor, Qwen's own FP8 is available above it; every speculative path is lossless by construction). Since v1.8 the flash lane serves an official SGLang image for this hardware with nothing added, serves 4 concurrent requests where it used to serve one, and got 14 to 25% of its decode back from one flag (--speculative-token-map, see below):

target model engine headline (measured here)
stock (default) Qwen3.8-27B NVFP4 SGLang + DFlash2 50 tok/s greedy median, 148+ aggregate at 8 streams, optional 1M context
uncensored Qwen3.8-27B abliterated NVFP4 SGLang + DFlash2 same speed and serving path as stock
fp8 Qwen3.8-27B FP8, Qwen's own release SGLang + DFlash2 the quantization reference: 108 tok/s aggregate at 8 streams, ~92K less KV pool
uncensored-fp8 Qwen3.8-27B abliterated FP8 SGLang + DFlash2 same serving path and same cost as fp8
flash (default of its lane) Qwen3.8-Flash-Next 176B hybrid MoE NVFP4 SGLang + NEXTN 47.9 tok/s on code, 47.1 on math, 29-31 on prose, 27.0 ms/tok on an agent loop, prefix caching, vision, 262K on ONE box
flash-uncensored the abliterated build of that same tree SGLang + NEXTN 205 of 206 shards identical in size to stock, so the same flags: 45-46 on code, 0 refusals of 5
flash-nvda the same 176B from NVIDIA's mixed-precision export SGLang + NEXTN ties the stock export inside the spread, measured here

The 27B path is the fastest configuration measured so far on GB10 (SGLang + NVFP4 + DFlash2 speculative decoding with deterministic kernels): 50 tok/s greedy median on ./bench.sh (code 41-47, reasoning 52-57, math peak 60), free prose 17-23 in any language, 135-148 tok/s aggregate at 8 concurrent streams, 258 at 32. Reproducible to the decimal across boots: see BENCHMARKS.md, "The boot lottery".

The flash path serves a model that does not otherwise fit: the 176B checkpoint's 47.7 GiB FP8 N-gram table is served from a sparse file on NVMe, read row by row by the gather kernel through GB10's host page tables, leaving the unified pool to the compute weights and a real KV cache. Until v1.7 that was a vendored patch of this repo's own; since v1.8 it is upstream (--ple-offload-backend file, sglang#37068) and the overlay is retired, along with the vendored sm_121 QSA kernel and the workaround for the GB10 MTP collapse. Prefix caching works (27k tokens re-served in 2.5 s against 12.0 s cold), decode is 47.9 tok/s on code and 47.1 on math single stream (29-31 on prose), prefill ~2,250 tok/s cold, and image input stays available. Since v1.8 it also takes --speculative-token-map, which hands the speculative draft the target's lm_head sliced to 65,536 rows instead of all 248,320: that removes 2.6 GiB from every engine step on a lane that is memory-bandwidth bound, and it is worth 14 to 25% of decode without changing what the model can say, because the target still verifies every drafted token over the whole vocabulary.

Whatever the target, you get the same surface: an OpenAI-compatible API on port 30000 (both lanes also speak the Anthropic protocol), a keepalive proxy for agent CLIs on 30001, and opencode works out of the box (the installer writes a ready-to-use provider config; the chat template ships pre-patched for agentic clients). The stack is built to grow: more targets, engines and drafters will slot into the same switch surface.

Quickstart

Requirements: DGX Spark or other GB10 machine (128 GB unified), stock DGX OS (Docker + NVIDIA container toolkit). Free disk: ~84 GB for a 27B target (39 under $HOME for checkpoints and caches, ~45 on the Docker partition for the 39 GB image; caching the other 27B targets adds ~21 GB per NVFP4 target and ~31 GB per FP8 one), **225 GB** for a flash target (~175 under $HOME: the ~126 GB checkpoint, ~124 for NVIDIA's export, plus the 47.7 GiB sparse file the N-gram table is served from and rewritten into on every boot; ~35 on the Docker partition for the 30 GB image).

One command, first install and updates alike (clones or updates ~/dgx-spark-qwen38, then runs the pinned installer):

curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | bash

Options ride on the bash side of the pipe (an env prefix on curl would not reach the installer):

curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | MODEL_CHOICE=uncensored CONTEXT_MODE=1m bash
curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | MODEL_CHOICE=flash bash

Or the explicit way:

git clone https://github.com/hasso5703/dgx-spark-qwen38.git
cd dgx-spark-qwen38
./install.sh            # image + checkpoints + systemd service, starts at every boot
./bench.sh              # verify your tok/s

First boot takes ~7-9 minutes for a 27B target (CUDA graph capture + kernel compilation, cached afterwards; later boots 5-7 min) and **12-15 minutes** for a flash target, every boot: the server writes the whole 47.7 GiB N-gram table into its file each time (measured here: 12 min 21 s to /health on a fresh table). Then:

Your choices, and how they combine

Everything below is optional and combinable. Variables ride on the bash side of the one-liner, flags go after bash -s --; with a clone, they go straight on ./install.sh.

Choice How Default Notes
Model MODEL_CHOICE=stock, uncensored, fp8, uncensored-fp8, flash, flash-uncensored, flash-nvda stock 27B NVFP4 stock or abliterated, the same pair in Qwen's FP8, or Flash-Next 176B in one of three NVFP4 exports (see the seven targets)
Reduced draft vocabulary SPEC_TOKEN_MAP_SIZE=65536, or 0 to serve without it 65536 flash only: hands the speculative draft the target's lm_head sliced to that many rows, which is 14 to 25% of decode and cannot change what the model may say
Flash serving tier FLASH_TIER=context, concurrency, throughput context flash only: 4 concurrent requests and a pool that takes a full 262K prompt, 8 requests at a third of the pool, or 24 without speculation
Context mode (27B) CONTEXT_MODE=native or 1m native 1m = 1,010,000 window, mem-fraction 0.70, proxy required (see the 1M section)
systemd service default, or --no-service service --no-service: foreground with ./run.sh, no sudo, 27B native only
Start now default, or --no-start starts install everything, start later with sudo systemctl start
opencode integration default, or --no-opencode on on = ready config + oc launcher + default model following every switch; off = none of that, your own opencode config is never touched. --with-opencode turns it back on
Ports PORT=, PROXY_PORT= 30000, 30001 agent clients use the proxy port
Storage HF_CACHE=, PLE_DIR= ~/.cache/huggingface, ~/flashnext-ple checkpoints, and the 48 GB flash PLE backing file
Clone location DIR= (one-liner only) ~/dgx-spark-qwen38 must be a clone of this repo on main
Cockpit dashboard dashboard/install-dashboard.sh, DASH_PORT=, DASH_BIND= not installed, loopback when installed opt-in, never run by install.sh; installs a sudoers allowlist, see "The cockpit"
Agent tab (opencode in the cockpit) dashboard/install-agent.sh, AGENT_PORT=, AGENT_BIND=, OPENCODE_PORT= not installed; relay on the tailnet address when installed opt-in, needs the cockpit and opencode on your PATH; opencode itself stays on loopback, see "The Agent tab"

Combinations that make sense:

# one-liner forms (variables on the bash side, flags after "bash -s --")
curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | bash                                  # 27B stock, native, service, opencode on
curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | CONTEXT_MODE=1m bash                  # 27B stock, 1M context
curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | MODEL_CHOICE=uncensored CONTEXT_MODE=1m bash
curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | MODEL_CHOICE=flash bash               # Flash-Next lane (service only)
curl -fsSL https://raw.githubusercontent.com/hasso5703/dgx-spark-qwen38/main/get.sh | bash -s -- --no-opencode              # API only, no opencode files
# clone forms
./install.sh --no-service && ./run.sh                    # no systemd, foreground, Ctrl+C stops it
CONTEXT_MODE=1m ./install.sh --no-start                  # prepare the 1M unit, start it yourself later
./install.sh --with-opencode                             # turn the opencode integration back on
./switch-model.sh stock | uncensored | flash             # change model later, no reinstall (then stop/start the units it prints)

Re-running the installer (upgrades included) remembers what you chose: the installed model, the context mode, the port, the HF cache, and the opencode on/off choice. Pass the variable or flag again only to change something. ./uninstall.sh --list shows everything the repo put on the box before removing anything.

What speed to expect

Speculative decoding accepts predictable tokens, so speed depends on what the model generates, not on one magic number:

Two instruments, both in the box, both reproducible. The headline 50 tok/s greedy median is ./bench.sh (streaming decode rate net of TTFT, the repo's historical headline instrument: v1.0-v1.1 measured ~36-40 on it, v1.2 measures 41-57 per probe). The table below is the harsher one: the frozen battery ./bench-matrix.sh (two-call wall-clock delta, comparable across engines and boxes):

What you generate (thinking on, battery v1) v1.2 (DFlash2, this repo) v1.1 (DSpark) Stable-MTP engines
Agentic coding (code, diffs, tool calls) 32-40 tok/s 28-36 24-28
Math & structured reasoning 41-44 38-42 24-33
Technical explanations (FR) 26 23-25 ~22
Free-form prose EN / FR / DE 22 / 20 / 17 17 / 14 / 13 17-20
8 concurrent streams, aggregate 135-148 100-104 ~92
32 concurrent streams, aggregate 258 not measured not measured

v1.2 wins every row of the frozen battery except eval-style math (parity with stock), including free prose, historically the weak spot of block drafters. Every number above is deterministic across boots (--disable-flashinfer-autotune, see BENCHMARKS.md "The boot lottery") and was re-verified after a full machine reboot, with output-quality canaries passing. This machine serves its own opencode sessions daily on this config (stretched to the 1M preset from the field report below): if something breaks, it breaks here first.

Quality, measured (not claimed). Same box, v1.2.1, thinking on:

Quality check Result
GSM8K, 200 problems 94.0% (188/200), exact parity with the DSpark profile
IFEval, 200 prompts 81.4% prompt-level / 87.4% instruction-level
tool-eval-bench, 69 scenarios 91/100 (Excellent), reproducible to the point across seeds
Independent users on this config 92-94/100 tool-calling (forum thread)
Losslessness token-identity study vs the pure model in BENCHMARKS.md ("The losslessness study")

Full study (methodology, engine-vs-engine matrix, an independent reproduction, the physics of the GB10 ceiling, and a frozen benchmark battery you can run against any engine, ./bench-matrix.sh): in BENCHMARKS.md.

⚠️ The GB10 unified-memory trap (read this before changing anything)

SGLang's memory accounting does not see 25-40 GB of transient allocations on GB10 unified memory (the flashinfer fp8 autotuner and CUDA graph capture allocate outside the tracked pool). Running --mem-fraction-static above 0.50, or running SGLang natively (outside Docker), can drive host available memory to zero: on a machine where SSH often rides on the same memory, that means a hard freeze only a power cycle fixes. We learned this the hard way.

This repo's service is safe by construction:

The 1m mode deliberately runs 0.70 inside the same docker caps, with the autotuner disabled: field-tested continuously on the reference box (~17 GiB host headroom). 0.80 was measured crashing under 3 concurrent requests (2 GiB free, Triton CUDA operation not permitted), and the 25-40 GB invisible-allocation bursts above all belong to native runs and the autotuner. Treat anything past 0.70 as livelock territory.

The SGLang cookbook pins 0.80 on DGX Spark, and that is not a contradiction. Its GB10 cells ran 48 configurations at ISL 8192 / OSL 1024, concurrency 1, boot-and-serve only, and 0.80 served every cell on every attempt; it rejects 0.85 because 0.85 of 128 GB leaves about 8 GB for the OS, exactly DGX OS earlyoom's SIGTERM threshold, and 15 of 48 cells were killed there (exit -15, no traceback, visible in journalctl -u earlyoom). This repo's 0.80 failure was measured under three concurrent requests on a box that also runs the operator's tools. One number is a single-stream boot-and-serve bound, the other is a multi-client operating point, and this repo optimises for the second. If you serve one stream on a dedicated box, the cookbook's 0.80 is the better-evidenced pin.

opencode integration

The installer writes a complete, ready-to-use opencode config at ~/.config/qwen38/opencode.json (the API key is referenced via {file:...}, no secret inside). It contains one provider per installed engine (qwen38 for the 27B pair, flashnext for flash), each with low / medium / xhigh reasoning-effort variants (no variant = the template's own default, xhigh), and its default model follows the installed target (./switch-model.sh re-points it on every switch):

# no opencode config yet? use it as-is:
mkdir -p ~/.config/opencode && cp ~/.config/qwen38/opencode.json ~/.config/opencode/opencode.json
# already have one? merge the "qwen38" (and/or "flashnext") provider block into it
opencode

Do not want any of it? ./install.sh --no-opencode (one-liner: | bash -s -- --no-opencode) installs the API only: no generated config, no oc launcher, and switch-model.sh never touches your opencode default model. The choice is remembered by later runs (marker ~/.config/qwen38/opencode.off); ./install.sh --with-opencode turns it back on. Your own ~/.config/opencode/opencode.json is never rewritten in either mode: the installer only merges the served lane's limits into it when the integration is on.

What the shipped config gets right for you:

  1. The hidden 32K output cap: opencode sends max_tokens = min(limit.output, OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX or 32000). Without that env var, a long thinking phase hits 32000 tokens, the turn ends silently (finish_reason: length, no text, no tool call) and you have to re-prompt. The installer ships an oc launcher (~/.local/bin/oc, skipped if an unrelated oc binary exists) that exports the right value and execs opencode --yolo: launch with oc instead of opencode and the cap sits above every output limit this repo declares, on every target and in either context mode. It is deliberately a ceiling rather than the installed target's own number, because limit.output is what follows a model switch: a ceiling copied fro

dgx-spark-qwen38 (hasso5703/dgx-spark-qwen38)

Searching Database…

这是什么?一个单命令安装器,在单台 NVIDIA DGX Spark(GB10)上为 Qwen3.8 模型家族搭建一套经过加固、重启后依然持久生效的 SGLang 服务栈:27B NVFP4 配置可达到 50 tok/s 的贪心中位数和最高 1M 上下文,176B Flash-Next MoE 模型也能装进这一台机器。
它是我导入的库,还是作为服务运行的东西?它是一个 shell 安装器和 systemd 服务,不是库:它会克隆仓库、运行 ./install.sh,并搭建一个在 30000 端口暴露 OpenAI 兼容 API 的服务器,外加 30001 端口上的智能体代理。
这个怎么运行?运行一行式安装器,它会克隆或更新 ~/dgx-spark-qwen38 并运行锁定的 ./install.sh;你也可以手动克隆并带参数运行 ./install.sh,或传入 --no-service 在前台运行而不用 sudo。
试用之前我需要什么硬件?一台真正的 DGX Spark 或其他 GB10 机器,拥有 128GB 统一内存,运行原版 DGX OS,且已装好 Docker 和 NVIDIA 容器工具包。
有什么我该知道的安装坑或磁盘/时间开销吗?27B 目标需要约 84GB 可用磁盘空间和 7-9 分钟的首次启动时间;176B flash 目标需要约 225GB 和 12-15 分钟,因为每次启动都要从零重写一张 47.7GiB 的 N-gram 表。
它需要特定 GPU 吗,还是任何 CUDA 显卡都行?它是专门为 DGX Spark 中的 NVIDIA GB10 芯片构建的,不面向通用 CUDA GPU;内核、卸载路径和 NVFP4 量化都是针对那种统一内存架构调优的。
Flash-Next 176B 模型是什么,它怎么能装进一台 DGX Spark?它是一个 176B 混合 MoE 检查点,其 47.7GiB 的 FP8 N-gram 表以逐行方式从 NVMe 上的稀疏文件供给,而不是加载进统一内存,从而把内存池留给计算权重和 KV 缓存。
opencode 集成是做什么的?安装器会在 ~/.config/qwen38/opencode.json 写入一份开箱即用的提供商配置,并为智能体客户端修补聊天模板,因此 opencode 无需配置即可对接本地服务器。
有没有无审查版本可选?有,“uncensored”和“flash-uncensored”目标提供相同检查点的 abliterated(去审查)版本,速度和服务路径与原版完全相同。
cockpit 仪表盘是什么?它是一个可选安装的本地 Web 仪表盘(dashboard/install-dashboard.sh),默认从不安装,只绑定到环回地址,并带有一个可选的 Agent 标签页,可通过 tailnet 转发 opencode。
Creator
hasso5703
Size
16.5 MB
Stars
227
Age
2mo
Commits · 90 Days