6-Node DGX Spark Ring Topology - NCCL Fails on Non-Adjacent Node Pairs (Routed RDMA)
TL;DR: 6x DGX Spark connected in a switchless ring (dual-rail, 200G per hop). Direct neighbor RDMA works perfectly. But vLLM/NCCL fails during initialization because it attempts to establish RDMA queue pairs between non-adjacent nodes too, which in a ring requires routed (multi-hop) RDMA — and that fails at the raw ibv_modify_qp level, independent of NCCL/vLLM. Looking for anyone who has solved this beyond the officially-supported 2-node/3-node configurations.
Setup
- 6x NVIDIA DGX Spark, each with ConnectX-7 (2 physical cards, 4 RoCE interfaces per node:
rocep1s0f0,rocep1s0f1,roceP2p1s0f0,roceP2p1s0f1). - Wired in a ring topology: each node connected to exactly two neighbors, using both ConnectX-7 cards per link (dual-rail, ~2x200G per hop).
- L3 point-to-point addressing (
/31per physical link), OSPF (FRR) for inter-node routing, stable per-node loopback/identity addresses on adummy0interface (to work around Gloo/NCCL treatingloas loopback-only). - Goal: vLLM with
--pipeline-parallel-size 6 --tensor-parallel-size 1(PP-only, since each node has a single GPU).
What works
- Full OSPF mesh, all 6 loopbacks reachable from every node.
ib_write_bwbetween directly connected neighbor pairs: full bandwidth, no issues.- Ray cluster forms correctly across all 6 nodes (
ray statusshows 6/6 GPUs). - NCCL/Gloo bootstrap over TCP (OOB) works fine once
GLOO_SOCKET_IFNAME/NCCL_SOCKET_IFNAMEare pinned to the stabledummy0identity address and routing hasset srcapplied via FRR route-maps (kernel was choosing whichever physical ring interface matched the route’s next-hop as source, causing asymmetric-path issues before this fix). GID table asymmetry between the two ConnectX-7 cards was also an issue (extra privacy-extension IPv6 addresses shifting the IPv4-mapped RoCEv2 GID index between cards) — fixed by disabling IPv6 on all ring interfaces and forcing a consistentNCCL_IB_GID_INDEXacross all 6 nodes.
What fails
During vLLM engine startup, NCCL’s world-group initialization (ensure_model_parallel_initialized → is_in_the_same_node_as self-test) attempts to establish RDMA connections between all rank pairs, not just adjacent ones in the PP schedule. For non-adjacent nodes (e.g., rank 0 ↔ rank 3), this requires the RDMA queue pair handshake to traverse intermediate nodes via IP forwarding. This consistently fails:
NCCL WARN Call to ibv_modify_qp failed with 110 Connection timed out, on dev rocep1s0f0:1,
curr state INIT, next state RTR, local GID index 3, local GID ::ffff:10.0.X.0, remote GID ::ffff:10.0.Y.0
I isolated this from NCCL/vLLM entirely using a raw RDMA bandwidth test between non-adjacent nodes’ identity addresses:
ib_write_bw -d rocep1s0f0 <non-adjacent-node-loopback> --report_gbits
Result:
Failed to modify QP to RTR
Unable to Connect the HCA's through the link
This confirms the failure is at the RDMA/verbs layer, not something NCCL-algorithm-specific — routed RoCE (through Linux kernel IP forwarding on intermediate nodes) does not satisfy whatever RC/RTR handshake requirements standard RoCEv2 QPs expect between non-adjacent hosts in this setup.
What I’ve found so far
I found community documentation (spark-vllm-docker / sparkrun project) confirming:
- Official/community support exists for 2-node (direct) and 3-node (switchless mesh) topologies only.
- Even the 3-node mesh requires a custom-patched NCCL build (not stock NCCL) with
NCCL_NET_PLUGIN=none,NCCL_IB_SUBNET_AWARE_ROUTING=1,NCCL_IB_MERGE_NICS=0. - For 4+ nodes, a physical switch is the recommended architecture.
Questions for the community
- Has anyone gotten a 4+ node switchless ring/mesh working with vLLM/NCCL — with or without a patched NCCL build?
- Is there a way to make vLLM/NCCL’s initialization skip the full-mesh connectivity self-test when only adjacent-node communication is actually needed for a given parallelism strategy (PP-only)?
- Are
NCCL_IB_SUBNET_AWARE_ROUTING/NCCL_IB_MERGE_NICS(from the patched build referenced above) available/effective in stock NCCL 2.28.9, or are they exclusive to that custom build? - For anyone who moved from switchless to a physical 200G switch for >3 node clusters — any gotchas with reusing existing node-to-node DAC/AOC cables, or does switch connectivity require different optics?
Happy to share full logs / configs if useful. Appreciate any pointers — currently blocked on this before we can validate our full 6-node pipeline-parallel deployment.