Skip to content

Instantly share code, notes, and snippets.

@randomvariable
Last active July 24, 2026 22:48
Show Gist options
  • Select an option

  • Save randomvariable/50ce8d88dbf111975176331e89888b2a to your computer and use it in GitHub Desktop.

Select an option

Save randomvariable/50ce8d88dbf111975176331e89888b2a to your computer and use it in GitHub Desktop.
Laguna-S-2.1-NVFP4 on DGX Spark (GB10): 2-node TP=2 vLLM at native 1M context over ConnectX-7 RoCE (ray backend), DFlash spec decoding + FlashInfer B12X NVFP4 MoE, fronted by llm-d EPP. Includes GB10 UMA wedge-safety rationale (why ray not mp, no VLLM_SKIP_INIT_MEMORY_CHECK).
# Laguna-S-2.1-NVFP4 on NVIDIA DGX Spark (GB10, sm_121a, arm64)
# 2-node TENSOR-PARALLEL (TP=2) vLLM serving at NATIVE 1M context, one engine
# sharded across two GB10 nodes over ConnectX-7 RoCE, with DFlash speculative
# decoding and the FlashInfer B12X NVFP4 MoE backend. Fronted by an llm-d
# Endpoint Picker (EPP) constrained to rank0.
#
# ─────────────────────────────────────────────────────────────────────────────
# WHY TP=2 (vs 2 independent replicas):
# One vLLM engine tensor-sharded across 2 DGX Spark nodes (1 GPU/node) gives a
# single UNIFIED KV pool and shards the weights (~35 GiB/node vs ~70 GiB on a
# single node), freeing memory for KV. At 1M context this yields materially
# higher concurrency than two independent TP=1 replicas, and lets one request
# use the combined KV pool.
#
# WHY RAY (NOT the `mp` distributed backend):
# On this exact GB10 node pair, `--distributed-executor-backend mp` WEDGED the
# nodes at both 262144 and 131072 context (UMA memory exhaustion during model
# init starves the kubelet -> node NotReady -> physical power-cycle). The `mp`
# configs that appear to "work" elsewhere rely on VLLM_SKIP_INIT_MEMORY_CHECK=1,
# which we DELIBERATELY DO NOT SET (see below). `ray` is the only multi-node
# backend that boots cleanly here WITHOUT that skip flag.
# - rank0 (ordinal 0) = Ray head + vLLM API leader.
# - rank1 (ordinal 1) = `ray start --block` worker (no vLLM process, no API).
# RayDistributedExecutor schedules the 2 TP workers across the Ray cluster.
#
# UMA WEDGE-SAFETY (GB10 shares ONE ~121 GiB pool between system RAM and GPU):
# - NO VLLM_SKIP_INIT_MEMORY_CHECK: keep vLLM's pre-flight guard so an
# over-allocation is a CLEAN pod-fail, not a kubelet-starving node wedge.
# - --enforce-eager: skip CUDA-graph capture (its transient buffer spike was a
# wedge trigger on UMA).
# - --kv-cache-memory pinned BELOW the "fully utilize" ceiling to leave headroom
# for allocation variance/fragmentation (do not pin KV to the exact max).
# - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True reduces fragmentation
# overshoot during the memory-profiling forward pass.
# - The b12x MoE workspace is shared across layers (vLLM PR #48698) so it no
# longer blows up per-layer during profile_run — the original wedge cause.
#
# NATIVE 1M CONTEXT:
# The published NVFP4 weights are native 1M checkpoints (long-context extension
# trained + quantization calibrated at 1,048,576 per the model card), but
# config.json ships the 256K profile by default. The fetcher Job patches
# config.json to the 1M profile post-download (idempotent, survives re-fetch):
# target: rope full_attention factor 32->128, attention_factor updated,
# max_position_embeddings 262144->1048576
# draft (DFlash head, rope=null): max_position_embeddings only.
# This RESTORES the trained config; it is NOT extrapolation past training.
#
# Placeholders to replace for your environment:
# <REGISTRY> your OCI registry host (image built from a vLLM source fork with
# sm_121a kernels + the b12x workspace-sharing patch)
# <IMAGE_TAG> your built image tag
# <NAMESPACE> target namespace
# <NODE_A>,<NODE_B> the two GB10 hostnames (one GPU each, cross-linked by RoCE)
# <ROCE_NAD> Multus NetworkAttachmentDefinition for the RoCE macvlan between
# the two nodes (mtu 9000). NCCL_IB_HCA below must name the RoCE
# devices actually present in the pod netns.
# <HF_TOKEN_SECRET> Secret holding a HuggingFace token (key HF_TOKEN)
# ─────────────────────────────────────────────────────────────────────────────
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: laguna-model-cache
namespace: <NAMESPACE>
spec:
# RWX so the fetcher writes once and both ranks read (shared filesystem).
accessModes: ["ReadWriteMany"]
storageClassName: <RWX_STORAGE_CLASS>
resources:
requests:
storage: 100Gi
---
# One-shot fetcher: downloads both checkpoints into the shared PVC and patches
# config.json to the native 1M profile. Job pod templates are immutable, so on
# any change delete+recreate the Job (or use your GitOps tool's Replace option).
apiVersion: batch/v1
kind: Job
metadata:
name: laguna-model-fetcher
namespace: <NAMESPACE>
spec:
ttlSecondsAfterFinished: 86400
backoffLimit: 20
manualSelector: true
selector:
matchLabels:
job: laguna-model-fetcher
template:
metadata:
labels:
job: laguna-model-fetcher
spec:
restartPolicy: OnFailure
nodeSelector:
kubernetes.io/arch: arm64
containers:
- name: fetcher
image: <REGISTRY>/vllm-spark-runtime:<IMAGE_TAG>
securityContext:
runAsUser: 0
command: [/usr/bin/python3]
args:
- -c
- |
import os
import json
import glob
import socket
from huggingface_hub import snapshot_download
# Force HuggingFace downloads over IPv4 (dual-stack egress workaround).
_getaddrinfo = socket.getaddrinfo
socket.getaddrinfo = lambda *a, **k: [x for x in _getaddrinfo(*a, **k) if x[0] == socket.AF_INET]
for model in (os.environ["TARGET_MODEL"], os.environ["DRAFT_MODEL"]):
snapshot = snapshot_download(model, max_workers=8)
print(f"snapshot={snapshot}")
# Restore the model's NATIVE 1M-context configuration (per the model
# card). The NVFP4 weights are native 1M checkpoints; config.json
# ships the 256K profile by default. Editing rope factor 32->128
# (+ attention_factor) and max_position_embeddings 262144->1048576
# restores the TRAINED 1M config -- NOT extrapolation. Idempotent;
# patched post-download so it survives every re-fetch.
def patch_config(model, rope):
cfg = sorted(glob.glob(f"/models/huggingface/hub/models--{model.replace('/','--')}/snapshots/*/config.json"))
if not cfg:
raise SystemExit(f"config.json not found for {model}")
path = cfg[-1]
c = json.load(open(path))
c["max_position_embeddings"] = 1048576
if rope:
fa = c.setdefault("rope_parameters", {}).setdefault("full_attention", {})
fa["factor"] = 128.0
fa["attention_factor"] = 1.4852030263919618
json.dump(c, open(path, "w"), indent=2)
print(f"patched 1M config: {path} (rope={'yes' if rope else 'max_pos_only'})")
# Target has YaRN full_attention rope; draft (DFlash head) has
# rope_parameters=null -> only raise max_position_embeddings.
patch_config(os.environ["TARGET_MODEL"], rope=True)
patch_config(os.environ["DRAFT_MODEL"], rope=False)
open("/models/.laguna_fetched", "w").close()
env:
- name: TARGET_MODEL
value: poolside/Laguna-S-2.1-NVFP4
- name: DRAFT_MODEL
value: poolside/Laguna-S-2.1-DFlash-NVFP4
- name: HF_HOME
value: /models/huggingface
- name: HF_HUB_OFFLINE
value: "0"
- name: HF_HUB_DISABLE_XET
value: "1"
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: <HF_TOKEN_SECRET>
key: HF_TOKEN
optional: true
volumeMounts:
- name: model-cache
mountPath: /models
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: laguna-model-cache
---
# Headless Service — ordinal DNS for the Ray head address (MASTER_ADDR).
apiVersion: v1
kind: Service
metadata:
name: laguna-tp2
namespace: <NAMESPACE>
labels:
app: laguna-tp2
spec:
clusterIP: None # headless — ordinal DNS for MASTER_ADDR (Ray head)
publishNotReadyAddresses: true # rank1 must resolve rank0 DNS before Ready
selector:
app: laguna-tp2
ports:
- name: http
port: 8000
targetPort: http
---
# rank0-only ClusterIP Service — the single API upstream (rank1 has no listener).
# Point your router / load balancer / InferencePool at THIS service.
apiVersion: v1
kind: Service
metadata:
name: laguna-tp2-rank0
namespace: <NAMESPACE>
labels:
app: laguna-tp2
spec:
selector:
app: laguna-tp2
apps.kubernetes.io/pod-index: "0"
ports:
- name: http
port: 8000
targetPort: http
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: laguna-tp2
namespace: <NAMESPACE>
labels:
app: laguna-tp2
spec:
serviceName: laguna-tp2
replicas: 2
# Parallel REQUIRED: rank0's Ray head polls for rank1 to join the cluster
# before launching vLLM; OrderedReady would deadlock (rank1 never created
# while rank0 is not Ready). podManagementPolicy is immutable after creation.
podManagementPolicy: Parallel
selector:
matchLabels:
app: laguna-tp2
template:
metadata:
labels:
app: laguna-tp2
annotations:
# RoCE macvlan attached into the pod netns (as net1) for NCCL cross-node
# all-reduce over the 200G ConnectX-7 fabric.
k8s.v1.cni.cncf.io/networks: <ROCE_NAD>
spec:
nodeSelector:
kubernetes.io/arch: arm64
# Pin one pod per node across the two GB10 nodes (TP=2, 1 GPU/node).
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values: [<NODE_A>, <NODE_B>]
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels: {app: laguna-tp2}
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 120
securityContext:
fsGroup: 0
initContainers:
- name: wait-model
image: <REGISTRY>/vllm-spark-runtime:<IMAGE_TAG>
command: [bash, -lc]
args:
- >-
until test -f /models/.laguna_fetched
&& ls /models/huggingface/hub/models--poolside--Laguna-S-2.1-NVFP4/snapshots/*/config.json >/dev/null 2>&1
&& ls /models/huggingface/hub/models--poolside--Laguna-S-2.1-DFlash-NVFP4/snapshots/*/config.json >/dev/null 2>&1;
do sleep 15; done
env:
- name: HF_HOME
value: /models/huggingface
volumeMounts:
- name: model-cache
mountPath: /models
containers:
- name: vllm
# Ray backend (NOT mp — mp wedged this GB10 pair at 262144 AND 131072;
# ray is the only no-skip-flag multi-node config that boots cleanly).
# rank0 = Ray head + vLLM API leader; rank1 = `ray start --block`.
# NOTE: the serve args below are a bash block, NOT a folded YAML scalar
# with inline `#` comments (a whitespace-preceded `#` in a folded scalar
# becomes a bash comment and silently truncates every following flag).
image: <REGISTRY>/vllm-spark-runtime:<IMAGE_TAG>
securityContext:
runAsUser: 0
capabilities:
# RDMA memory registration pins pages; without IPC_LOCK
# ibv_reg_mr_iova2 fails "Cannot allocate memory" -> NCCL error.
add: [IPC_LOCK]
command: [bash, -lc]
args:
- |
if [ "${NODE_RANK}" = "0" ]; then
ray start --head --port 6380 --num-cpus 8 \
--node-ip-address "${VLLM_HOST_IP}" \
--include-dashboard=false --disable-usage-stats &
NODES=0
for i in $(seq 1 60); do
NODES=$(ray status 2>/dev/null | grep -Ec '^\s*[0-9]+\s+node_' || true)
if [ "${NODES}" -ge 2 ]; then
echo "ray cluster at ${NODES} nodes; launching vllm serve"
break
fi
echo "waiting for rank1 to join ray cluster (attempt ${i}/60, nodes=${NODES})"
sleep 2
done
if [ "${NODES}" -lt 2 ]; then
echo "rank1 did not join ray cluster within ~120s; aborting" >&2
exit 1
fi
exec vllm serve poolside/Laguna-S-2.1-NVFP4 --served-model-name laguna-s-2.1 \
--host 0.0.0.0 --port 8000 \
--tensor-parallel-size 2 --pipeline-parallel-size 1 \
--distributed-executor-backend ray \
--speculative-config='{"model":"poolside/Laguna-S-2.1-DFlash-NVFP4","num_speculative_tokens":15,"method":"dflash"}' \
--max-model-len 1048576 --gpu-memory-utilization 0.80 --enforce-eager \
--kv-cache-memory 68719476736 \
--max-num-seqs 32 --max-num-batched-tokens 10240 --trust-remote-code \
--enable-prefix-caching --enable-prompt-tokens-details --enable-chunked-prefill \
--enable-auto-tool-choice --tool-call-parser poolside_v1 \
--reasoning-parser poolside_v1 --moe-backend flashinfer_b12x \
--default-chat-template-kwargs '{"enable_thinking":true}' \
--override-generation-config '{"temperature":0.7,"top_p":0.95}' \
--disable-access-log-for-endpoints /metrics
else
exec ray start --block --address="${MASTER_ADDR}:6380" \
--num-cpus 8 --disable-usage-stats \
--node-ip-address "${VLLM_HOST_IP}"
fi
env:
- name: NODE_RANK
valueFrom:
fieldRef:
fieldPath: metadata.labels['apps.kubernetes.io/pod-index']
- name: MASTER_ADDR
value: laguna-tp2-0.laguna-tp2.<NAMESPACE>.svc.cluster.local
- name: VLLM_HOST_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- {name: HF_HOME, value: /models/huggingface}
- {name: HF_HUB_OFFLINE, value: "1"}
# flashinfer JIT does os.makedirs(~/.cache); point HOME at a writable dir.
- {name: HOME, value: /opt/vllm}
# b12x native NVFP4 MoE (FlashInferB12xExperts, CuTe DSL kernel path).
# CUTE_DSL_ARCH selects the sm_121a CuTe DSL kernel variant at runtime.
- {name: CUTE_DSL_ARCH, value: sm_121a}
# Belt-and-suspenders in case a stale 256K config.json is still cached
# (the fetcher normally patches config to the native 1M profile).
- {name: VLLM_ALLOW_LONG_MAX_MODEL_LEN, value: "1"}
- {name: MAX_JOBS, value: "4"}
- {name: PATH, value: /usr/local/cuda/bin:/opt/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}
# expandable_segments reduces UMA fragmentation overshoot during the
# memory-profiling forward pass.
- {name: PYTORCH_CUDA_ALLOC_CONF, value: expandable_segments:True}
# Ray executor knobs. memory_monitor_refresh_ms=0 stops Ray's own
# memory monitor from competing with vLLM's budgeting during init.
- {name: RAY_memory_monitor_refresh_ms, value: "0"}
- {name: RAY_num_prestart_python_workers, value: "0"}
- {name: RAY_object_store_memory, value: "1073741824"}
# NCCL — pod netns: control plane on eth0; data plane via RoCE verbs.
# GB10 has no GPUDirect RDMA; do NOT set NCCL_NET_GDR_LEVEL.
# NCCL_IB_HCA must name the RoCE devices present in the pod netns.
- {name: NCCL_NET, value: IB}
- {name: NCCL_IB_DISABLE, value: "0"}
- {name: NCCL_IB_HCA, value: "<ROCE_HCA_0>,<ROCE_HCA_1>"}
- {name: NCCL_SOCKET_IFNAME, value: eth0}
# Gloo reads its OWN iface var (not NCCL_SOCKET_IFNAME); without this
# it can pick the host management NIC which is absent in the pod netns.
- {name: GLOO_SOCKET_IFNAME, value: eth0}
- {name: NCCL_CROSS_NIC, value: "1"}
- {name: NCCL_CUMEM_ENABLE, value: "0"}
- {name: NCCL_IGNORE_CPU_AFFINITY, value: "1"}
- {name: NCCL_DEBUG, value: INFO}
- {name: NCCL_NVLS_ENABLE, value: "0"}
# NOTE: VLLM_SKIP_INIT_MEMORY_CHECK is intentionally NOT set — the
# guard turns a UMA overshoot into a clean pod-fail rather than a
# node wedge. This is WHY ray (not mp) is used here.
ports:
- {name: http, containerPort: 8000}
# Probes branch on rank: rank1 runs `ray start --block` (no vLLM
# process, no API listener), so rank1 health = "ray process alive".
# Killing rank1 tears down the Ray cluster rank0 depends on.
startupProbe:
exec:
command:
- sh
- -c
- |
if [ "${NODE_RANK}" = "0" ]; then
python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/v1/models', timeout=4)"
else
python3 -c "import glob,sys; sys.exit(0 if any(b'ray' in open(c,'rb').read() for c in glob.glob('/proc/[0-9]*/cmdline')) else 1)"
fi
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 240 # generous headroom for the long 1M NVFP4 boot
readinessProbe:
exec:
command:
- sh
- -c
- |
if [ "${NODE_RANK}" = "0" ]; then
python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/v1/models', timeout=3)"
else
python3 -c "import glob,sys; sys.exit(0 if any(b'ray' in open(c,'rb').read() for c in glob.glob('/proc/[0-9]*/cmdline')) else 1)"
fi
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
livenessProbe:
exec:
command:
- sh
- -c
- |
if [ "${NODE_RANK}" = "0" ]; then
python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=8)"
else
python3 -c "import glob,sys; sys.exit(0 if any(b'ray' in open(c,'rb').read() for c in glob.glob('/proc/[0-9]*/cmdline')) else 1)"
fi
periodSeconds: 30
timeoutSeconds: 12
failureThreshold: 10
resources:
# rdma/<...> is the RDMA device-plugin resource for the RoCE fabric.
requests: {cpu: "8", memory: 105Gi, nvidia.com/gpu: "4", rdma/roce: "63"}
limits: {memory: 105Gi, nvidia.com/gpu: "4", rdma/roce: "63"}
volumeMounts:
- {name: shm, mountPath: /dev/shm}
- {name: model-cache, mountPath: /models}
volumes:
- name: shm
emptyDir:
medium: Memory
sizeLimit: 64Gi
- name: model-cache
persistentVolumeClaim:
claimName: laguna-model-cache
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment