The NCCL Debug That Changed Everything
In the middle of a sprawling session to deploy large language models across a cluster of NVIDIA Blackwell GPUs, one message stands out as a quiet turning point — a moment when the fog of a stubborn, hours-long hang began to clear. Message <msg id=6157> is deceptively brief: a single bash command wrapped in the assistant's characteristic diagnostic style. But within its few lines lies a masterclass in systematic debugging, the kind that separates surface-level fixes from genuine root-cause understanding.
The Context: A Server That Won't Start
To appreciate what this message accomplishes, we must first understand the situation that led to it. The assistant had been working through an ambitious deployment pipeline on a Proxmox host with 8× NVIDIA RTX PRO 6000 Blackwell GPUs. The machine had recently been reconfigured: 4 GPUs were bound to an LXC container running SGLang (a high-performance LLM serving framework), while the other 4 were passed through to a confidential computing VM via vfio-pci. The model being deployed was Qwen3.5-122B-A10B, a 234 GB BF16 model — a significant shift from the earlier Qwen3.5-397B NVFP4 that had been running on the same hardware.
The server was failing to start. Every attempt would log "Init torch distributed begin" and then sit there, frozen, for minutes on end. The assistant had already resolved one issue — a driver version mismatch where the container's NVIDIA userspace libraries were at version 565 while the host kernel module was at 590 — but even after fixing that, the hang persisted. The NCCL-based distributed initialization that SGLang uses to coordinate tensor parallelism across the 4 GPUs was simply not completing.
The Message Itself: A Deliberate Diagnostic Probe
The subject message reads:
No port conflicts. Let me try launching manually with NCCL debug to see where it hangs:
>
``bash ssh root@10.1.230.174 'NCCL_DEBUG=INFO timeout 60 ~/ml-env/bin/python3 -m sglang.launch_server --model-path /shared/models/Qwen3.5-122B-A10B --served-model-name qwen3.5-122b --tp 4 --trust-remote-code --host 0.0.0.0 --attention-backend triton --kv-cache-dtype bf16 --reasoning-parser qwen3 --tool-call-parser qwen3_coder --disable-custom-all-reduce 2>&1 | tail -40' ``
>
`` llm-two:5948:5948 [0] NCCL INFO Initialized NET plugin Socket llm-two:5948:5948 [0] NCCL INFO Assigned NET plugin Socket to comm llm-two:5948:5948 [0] NCCL INFO Using network Socket llm-two:5948:5948 [0] NCCL INFO [Rank 0] ncclCommInitRank comm 0x47e49e30 rank 0 nranks 4 cudaDev 0 nvmlDev 0 busId 1000 commId 0xb23ca3b0f9a12be8 - Init START llm-two:5948:5948 [0] NCCL INFO RAS client listening socket at ::1<28028> llm-two:5948:5948 [0] NCCL INFO Bootstrap timings total 0.004433 (create 0.000043, s... ``
This is not a random command. It is a carefully constructed diagnostic probe, and every element of it reveals the assistant's thinking.
Why This Message Was Written: The Reasoning and Motivation
The assistant had just finished checking for port conflicts on the NCCL rendezvous ports (<msg id=6156>) and found nothing blocking. But the server was still hanging. At this point, the assistant faced a branching decision tree: was the hang inside NCCL itself, or somewhere after NCCL initialization? The two possibilities would lead to entirely different debugging paths.
The key insight is that the assistant had been watching the server hang through the lens of systemd journal logs, which only showed the SGLang application-level messages. Those logs would print "Init torch distributed begin" and then nothing more. From that limited view, it was natural to assume NCCL itself was hanging — after all, "torch distributed" is synonymous with NCCL in most practitioners' minds.
But the assistant recognized that this assumption needed to be tested, not trusted. The NCCL_DEBUG=INFO environment variable is NCCL's built-in diagnostic logging mechanism. Setting it to INFO produces a detailed trace of every step NCCL takes during initialization: plugin loading, socket binding, rank discovery, communicator creation, and bootstrap synchronization. By running the launch command directly (not through systemd) with a 60-second timeout and piping through tail -40, the assistant created a controlled experiment that would capture NCCL's internal state without the noise of the full application log.
The --disable-custom-all-reduce flag is also significant. This tells SGLang to skip its custom all-reduce implementation and use NCCL's native one. The assistant was systematically eliminating variables: custom all-reduce could itself be a source of hangs, so removing it from the equation simplified the diagnostic surface.
What the Output Revealed: A Critical Assumption Shattered
The NCCL debug output is the real payload of this message, and it tells a story that the assistant immediately recognized. NCCL initializes cleanly:
- NET plugin Socket — The network transport layer (Socket) is initialized successfully.
- Assigned NET plugin Socket to comm — NCCL assigns this transport to the communicator.
- Using network Socket — Confirms the transport selection.
- ncclCommInitRank comm ... Init START — NCCL begins creating the communicator for rank 0 out of 4 ranks, on CUDA device 0, with bus ID 1000.
- RAS client listening socket at ::1<28028> — NCCL's reliability, availability, and serviceability (RAS) subsystem sets up its listening socket.
- Bootstrap timings total 0.004433 — The bootstrap phase completed in just 4.4 milliseconds. That last line is the bombshell. NCCL's bootstrap — the phase where all ranks discover each other and synchronize — completed in under 5 milliseconds. This is not a hang. This is a perfectly healthy NCCL initialization. The assistant's reaction in the very next message (
<msg id=6158>) confirms the pivot: "NCCL init actually completed fine — it's using P2P/IPC with Ring+LL. The hang must be happening after NCCL init, likely during weight loading or triton kernel compilation." This is the moment the debugging trajectory changes. The assumption that NCCL was the problem is discarded, and a new hypothesis takes its place: something after NCCL init is blocking. The hang could be intorch.distributed.init_process_group, in model weight loading, in Triton kernel compilation, or in some other initialization step that the SGLang framework performs after NCCL is ready.
The Thinking Process Visible in the Reasoning
The assistant's reasoning in this message is a textbook example of the scientific method applied to systems debugging:
Hypothesis generation: The server hangs at "Init torch distributed begin." The most likely culprit is NCCL initialization, since that's what "torch distributed" typically means.
Test design: Run the exact same SGLang launch command, but with NCCL_DEBUG=INFO to expose NCCL's internal state. Add a 60-second timeout to prevent the test from hanging indefinitely. Use tail -40 to focus on the final output (the most relevant diagnostic information).
Data collection: Execute the command via SSH on the remote container, capturing stdout/stderr.
Analysis: The NCCL debug output shows clean initialization with sub-5ms bootstrap timing. NCCL is not the problem.
New hypothesis formation: The hang must be after NCCL init — in torch distributed's init_process_group, model loading, or kernel compilation.
This cycle of hypothesis → test → data → analysis → new hypothesis is the engine of effective debugging, and this message captures one complete iteration of it.
Assumptions Made and Mistakes Encountered
The assistant operated under several assumptions in this message, some explicit and some implicit:
Explicit assumption: The hang was in NCCL initialization. This turned out to be wrong — NCCL initialized perfectly. But the assumption was valuable precisely because it was testable. The assistant didn't believe NCCL was the problem; they suspected it and designed an experiment to confirm or refute that suspicion.
Implicit assumption: The NCCL debug output would be visible and informative. This was correct — NCCL_DEBUG=INFO produced exactly the right level of detail.
Implicit assumption: The timeout of 60 seconds was sufficient to capture NCCL init (which takes milliseconds) without waiting through the actual hang. This was correct — NCCL completed in milliseconds, and the timeout was never reached.
Implicit assumption: The --disable-custom-all-reduce flag wouldn't change NCCL's initialization behavior. This was a reasonable simplification — custom all-reduce operates at a higher level than NCCL's communicator creation.
The only "mistake" in this message is the incorrect initial hypothesis, but that's not really a mistake — it's the starting point of any investigation. The real error would have been to not test the assumption and instead keep tweaking NCCL environment variables blindly, which is exactly what the assistant had been doing in the preceding messages.
Input Knowledge Required
To fully understand this message, a reader needs knowledge across several domains:
NCCL (NVIDIA Collective Communications Library): Understanding that NCCL is the backbone of multi-GPU communication in PyTorch, that it uses a bootstrap phase for rank discovery, and that NCCL_DEBUG=INFO exposes its internal state.
SGLang architecture: Knowing that SGLang uses tensor parallelism (TP) to shard models across GPUs, that it initializes NCCL communicators during startup, and that the "Init torch distributed begin" log message marks the start of this process.
CUDA and GPU topology: Understanding bus IDs (busId 1000), NVML device indices, and how NCCL maps logical ranks to physical GPUs.
Linux process management: Knowing that timeout 60 will kill a hung process, that nohup and & are used for backgrounding, and that SSH can execute remote commands with environment variables.
The Qwen3.5-122B model: Understanding that this is a 234 GB BF16 model that requires 4× 96 GB GPUs for tensor parallelism, and that its MoE (Mixture of Experts) architecture adds complexity to weight loading.
Output Knowledge Created
This message produces several valuable pieces of knowledge:
Primary finding: NCCL initialization is not the cause of the hang. The NCCL communicator for rank 0 initializes successfully, with bootstrap completing in 4.4 milliseconds.
Secondary finding: The NCCL transport is using Socket (not InfiniBand/IB), which is expected for a PCIe-connected multi-GPU system where GPUs communicate through system memory rather than dedicated interconnects.
Tertiary finding: NCCL's RAS subsystem is active, listening on port 28028 on localhost.
Methodological knowledge: The technique of running NCCL_DEBUG=INFO with a timeout is a reusable diagnostic pattern for any NCCL-related hang.
Negative knowledge: The --disable-custom-all-reduce flag doesn't prevent the hang, ruling out custom all-reduce as the cause.
The Broader Significance
This message is a classic "the problem is not where you think it is" moment. The assistant had been chasing NCCL configuration issues — checking port conflicts, fixing driver mismatches, examining NCCL environment variables in sitecustomize.py — all under the assumption that NCCL was failing. The NCCL debug output shattered that assumption in a single command.
What followed in subsequent messages was a deeper investigation that eventually traced the hang to a far more subtle issue: P2P DMA corruption under SEV-SNP IOMMU translation. The SEV-SNP (Secure Encrypted Virtualization with Secure Nested Paging) configuration on the Proxmox host enabled full IOMMU translation (amd_iommu=on), which broke GPU-to-GPU P2P DMA. Every P2P transfer produced corrupted data, causing NCCL to hang — but not during NCCL's own initialization. The hang occurred later, when NCCL attempted to use P2P for actual data transfer during model weight distribution.
The fix was NCCL_P2P_DISABLE=1, which forced NCCL to use SHM (shared memory) transport instead of P2P DMA. This is a workaround, not a true fix — the assistant later investigated BIOS-level options to re-enable P2P DMA under IOMMU translation — but it got the server running, achieving 108 tok/s single-stream and up to 2,800 tok/s at high concurrency.
Conclusion
Message <msg id=6157> is a small but perfect example of what makes great systems debugging: a clear hypothesis, a well-designed experiment, clean data collection, and the intellectual honesty to abandon a comfortable assumption when the evidence contradicts it. The NCCL debug output — those six lines showing a healthy 4.4-millisecond bootstrap — redirected the entire investigation and ultimately led to the real root cause. In a session spanning hundreds of messages across GPU topology reconfiguration, driver upgrades, model swaps, and performance tuning, this one diagnostic command was the pivot point that turned a stuck server into a working deployment.