Got this!
Thank you @tonyd615 - this will be my main model now!
Here are the exact fixes, including several that aren’t in the README.
1. OOM at load (“gets to 121/122 GB and earlyoom kills it”) — @0rand
That’s earlyoom killing vLLM, not a vLLM bug. Run systemctl stop earlyoom before launch (or raise its -m threshold). It runs near-ceiling, so the recipe’s stability env vars matter:
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
RAY_memory_monitor_refresh_ms=0 (stops Ray’s 95% monitor from false-killing the head after warmup)
- Cap the Ray object store to 1 GiB per node:
ray start … --object-store-memory=1073741824. Uncapped, Ray reserves a big plasma store and steals unified memory, which OOMs.
2. Which base image — @0rand
Don’t build on 0.23 (it diverges). Use Tony’s published image directly:
ghcr.io/tonyd2wild/mimo-v2.5-tp2-1m-nvfp4kv:20260620
It’s the patched vLLM dev build (0.21.1rc1.dev85+gd87ee1893) and the 6 required mods are vendored in recipe/mods/. Apply them with apply-mods.sh after the container is up. A stock pip install vllm will reject --kv-cache-dtype nvfp4 / triton_attn_diffkv.
If a node can’t pull from ghcr (DNS/egress), pull on the node that can and ship it over your fast link:
docker save … | ssh <other-node> docker load
3. Ray vs. vLLM-distributed worker crash — @0rand
For 2 nodes you must use Ray. The mp (multiprocessing) executor is single-host: it can’t span two boxes, so that worker crash is expected. (Re the “TP=2 only works single-node” point: TP=2 across two boxes works fine, but only via Ray, not the mp executor.) Keep --distributed-executor-backend ray.
4. Gotchas not in the README
These are very likely your “worker still crashes”:
Raylet aborts with Timed out waiting for file …/metrics_export_port_…: the Ray dashboard agent hangs ~20 s on socket.getfqdn() (reverse-DNS timeout) and the raylet’s ~15 s wait fires first. Fix: add the hostname to /etc/hosts inside the worker container: echo "127.0.0.1 $(hostname)" >> /etc/hosts. The agent then registers in under 1 s.
gloo … Unable to find address for: <iface> during init_distributed_environment: gloo picked a DOWN or wrong NIC. Pin gloo and NCCL to your RoCE interface on both nodes: export GLOO_SOCKET_IFNAME=<roce-iface> NCCL_SOCKET_IFNAME=<roce-iface> (on our Sparks both nodes name it enP2p1s0f0np0; check ip -br addr).
Placement group never fills: No available node types can fulfill resource request {'GPU':1.0,'node:10.0.0.x':…}: vLLM auto-detected the wrong host IP (default-route NIC, not RoCE). Export VLLM_HOST_IP=<this node's RoCE IP> before ray start and before vllm serve (head = head RoCE IP, worker = worker RoCE IP).
Start order: worker container → Ray head → Ray worker (join) → wait for 2.0/2.0 GPU → launch vllm serve from the head.
In our tool/agent testing the NVFP4 weights + NVFP4 KV were effectively lossless vs the AWQ-int4 baseline, and the NVFP4 KV is exactly what buys the 1M context (KV pool ~2M tokens at gpu-util 0.84). Solid but task-dependent, so run your own eval.
Results: ~28–30 tok/s decode, 1M context verified