Skip to content

Spectrum-X rails inside Kubernetes

S4·E1The rail that verified clean and moved nothing · The customer's new pilot cluster, on a bridge call, eleven days before acceptance

S4·E1Analyze~30 minsources checked todayverified against NVIDIA Network Operator v26.7.0 doc set (release notes, platform support, Spectrum-X overview / quick start / components / verify-and-troubleshoot), re-fetched 2026-09-07

Builds on: NIC firmware config in Kubernetes: RoCE, PFC and reboots, Network Operator and the NicClusterPolicy

Before you read: what do you already know?

3 quick questions. Wrong answers are fine and expected; trying first makes the lesson stick.

After this lesson you can

  • Explain what the Spectrum-X Operator adds on top of Multus, SR-IOV and the Network Operator: NIC discovery, rail configuration and schedulable rail resources.
  • Choose a multiplaneMode from the hardware in front of you and state whether DOCA xPlane will be deployed.
  • Predict which rails get VRF isolation from the IPAM reference alone, and write the traffic command that survives it.
  • Diagnose a rail that reports syncStatus Failed using the operator's own verification commands.

Episode 1 — The rail that verified clean and moved nothing

The situation · The customer's new pilot cluster, on a bridge call, eleven days before acceptance

The customer’s network lead has his grid-ruled notebook open and one sentence ready: show me the counter. The Dell SE is on mute with a cold coffee and row 14 of the promise spreadsheet, which reads “rails proven, week one”. Twelve nodes, one fabric, an acceptance test on a Friday, and this morning two pods will not speak to each other.

You ask for three outputs. The rail pool’s syncStatus reads Succeeded, and .status.nodeStates agrees node by node.[4] Inside spectrum-x-test, rdma link lists one RDMA device per configured rail.[4] Then ib_write_bw -R between two pods reports no connection at all.

Everything that is supposed to be true is true, and nothing moves — which is the feature working exactly as designed. Spectrum-X exists to keep the GPU-to-GPU east-west network lossless, and part of keeping rails from interfering with each other is that the operator’s VRF meta-plugin isolates each rail in its own VRF, named after the rail topology entry.[3] A routing table that rail traffic cannot leak out of is also a routing table a benchmark cannot wander into: RDMA-CM is not VRF-aware, so a test that uses it has to be launched inside the VRF context, and a workload that does that needs IPC_LOCK, NET_ADMIN, DAC_OVERRIDE and BPF.[4]

A rail that verifies is not a rail that carries traffic — run the test where the traffic lives.

You have ninety seconds before someone dials the switch vendor. What you need is the layer map: which objects the Spectrum-X Operator owns, and which are plain Linux underneath.

1What the Spectrum-X Operator adds on top of everything so far

By this point in the course a pod can already get a second interface on an SR-IOV VF, with RDMA, scheduled by an extended resource. Spectrum-X is the layer above that: it “combines Spectrum switches with BlueField-3 SuperNIC, ConnectX-7, and ConnectX-8 NICs to deliver high-bandwidth, lossless RoCE for the GPU-to-GPU compute (east-west) network”, and the operator’s job inside Kubernetes is “discovering the NICs, configuring rails, and exposing them to pods as schedulable resources”.[1] Nothing new appears in the Kubernetes API: a rail is still an extended resource, and the test pod still requests nvidia.com/rail0: "1" and nvidia.com/rail1: "1" with the annotation k8s.v1.cni.cncf.io/networks: rail0,rail1.[2]

Three moving parts carry the whole feature. A flow-controller DaemonSet programs per-rail OVS flows and owns the SpectrumXRailPoolConfig CRD; it is “essential for any Spectrum-X mode (single-plane, swplb, hwplb)”.[3] A NIC tuning profile ships as a versioned ConfigMap and is applied through spectrumXOptimized in a NicConfigurationTemplate.[1] And a VRF meta-plugin optionally isolates each rail in its own routing table.[3]

The hardware pairing is worth stating out loud in a Dell conversation, because it is the same platform matrix as the rest of the operator: ConnectX-6 through ConnectX-9, BlueField-3 and BlueField-3 SuperNIC in NIC mode only, up to 800 Gb/s.[6] A BlueField in DPU mode is not a Spectrum-X rail NIC as far as this operator is concerned.

HGX B300 node (e.g. Dell PowerEdge XE9780)N-S onlyGPU0B300GPU1B300GPU2B300GPU3B300GPU4B300GPU5B300GPU6B300GPU7B300CX-8800GCX-8800GCX-8800GCX-8800GCX-8800GCX-8800GCX-8800GCX-8800GBlueField-3 DPU (B3240)DPU/ECPF mode · N-S · OVS-DOCA/HBN/DTSE-W leaves: SN5600Spectrum-X · RoCE · adaptive routingN-S leafstorage · mgmt · tenant

HGX B300 node in a Spectrum-X AI factory

Reference architecture per node: 8 Blackwell Ultra GPUs, "Eight NVIDIA ConnectX-8 SuperNICs per NVIDIA HGX B300 baseboard" at a "1:1 GPU-to-NIC ratio" for east-west, and "One NVIDIA BlueField-3 DPU per server" in "ECPF or DPU mode" for north-south.

Why it matters. Dell's HGX B300 chassis are PowerEdge XE9780/XE9785; Dell lists its Partner DPU (BlueField-3 dual-port 400GbE) as compatible with XE9780. Spectrum-X validated stack v2.3.1 (Sep 2026): Cumulus 5.18.1, BF-3 FW 32.50.1002, DOCA-Host 3.5.0-082, NCCL 2.30.7.

FAE note. Click a GPU/NIC pair, the DPU, or either fabric. The key question customers ask: why is the DPU not in the GPU traffic path?

Source: docs.nvidia.com

Switch to the AI-factory view and trace one rail from the GPU through the SuperNIC to the leaf. Nothing here runs on Franco's lab hardware — this is the picture you defend in a meeting.

2multiplaneMode, and the xPlane rule you can read off the rail topology

multiplaneMode takes three values. none is single-plane and covers BlueField-3 and ConnectX-7/ConnectX-8 on H100, H200 and B200, plus GB200. hwplb is Hardware Multiplane on ConnectX-8 with B300/GB300 and is documented as “recommended mode”. swplb is Software Multiplane on the same hardware.[1] Hardware Multiplane pulls in a component: “DOCA xPlane is a required component.”[1] Hardware Multiplane with DOCA xPlane went GA on ConnectX-8 in 26.7.0.[5]

You do not have to remember which mode deploys xPlane, because the rail topology tells you. xPlane is a DaemonSet deployed “per rail topology entry that selects more than one PF”.[3] That gives a table you can reconstruct from first principles:

Mode Shape of railTopology xPlane
hwplb one entry per rail, listing every plane’s PF yes
swplb one entry per rail-plane, single PF no
none one entry per rail, single PF no
[3]

For hardware multiplane the operator waits for the selected PFs to enter switchdev mode before it creates the br-xplane bridge.[3] That wait is why a hwplb cluster can sit for minutes with nothing apparently happening and still be healthy.

The version story is a straight mapping and an FAE should have it memorised: RA 2.3 with operator 26.7.x (GA), RA 2.1 with 26.4.x (Tech Preview), RA 2.1 with 26.1.x (GA).[1] When a customer names an RA version, you can name the operator version, and vice versa.

3The 26.7.0 profile ConfigMap: a name that must match character for character

Up to 26.4, spectrumXOptimized.version selected a built-in Reference Architecture. From 26.7.0 the NIC tuning ships as a versioned Spectrum-X profile ConfigMap, and spectrumXOptimized.version names that ConfigMap.[1][5] In the quick start the block reads {enabled: true, version: "spectrum-x-ra2.3-profile", overlay: "none", multiplaneMode: "none", numberOfPlanes: 1} inside a NicConfigurationTemplate.[2]

The verification page is blunt about the matching rule. List the candidates with kubectl get configmap -A -l network.nvidia.com/operator.nic-configuration.spectrum-x-profile; “the label value is ignored — only the key must be present”, and the ConfigMap name is what spectrumXOptimized.version must match, character for character.[4] Confirm the daemon actually loaded it with kubectl -n nvidia-network-operator logs -l app.kubernetes.io/name=nic-configuration-daemon | grep "Loaded Spectrum-X profile".[4]

Three of the documented failure rows come straight out of this one field:[4]

Symptom Cause and fix
spectrum-x config version <name> not found ConfigMap missing, or missing the watch label, or the name does not match exactly
Parameters silently skipped The profile has no mlxConfig entry for that device ID or multiplane mode
Two profiles, unpredictable results Two ConfigMaps share a name across namespaces; use unique names

The rest of the NIC-side tuning is the ordinary NicConfigurationTemplate you already know, where pciPerformanceOptimized, roceOptimized and gpuDirectOptimized live — and where pciPerformanceOptimized.maxAccOutRead is now documented as “Deprecated, ignored”.[7][5]

4SpectrumXRailPoolConfig, the VRF rule and the MTU that must agree three times

The rail pool is one CR. In spectrumx.nvidia.com/v1alpha2, the quick start sets draEnabled: false, networkNamespace: default, numVfs: 1, and a railTopology list whose entries look like {name: rail0, nicSelector: {pfNames: ["net_rail0"]}, cidrPoolRef: rail-0, mtu: 9216}.[2] The pfNames values are not accidents: the Spectrum-X flow adds a NicInterfaceNameTemplate with netDevicePrefix: "net_rail%rail_id%" and rdmaDevicePrefix: "rdma_rail%rail_id%", so the interface names are manufactured to be selectable per rail.[2]

The IPAM side is where the VRF rule bites. The VRF meta-plugin “isolates each rail in its own VRF, named after the rail topology entry”, and VRFs apply only to rails whose cidrPoolRef resolves to an IPv6 CIDRPool; rails using inline railTopology[].ipam do not get VRF isolation at all.[3] IPv6 rail isolation via the VRF CNI meta-plugin is itself new in 26.7.0.[5] Each rail also gets the RDMA CNI with rdmaQoS: {tos: 96, tc: 96}, and the pod’s RDMA device is named rdma_<rail topology name>.[3]

Rail MTU in NVIDIA’s own quick start is 9216.[2] That number only helps if the whole path agrees with it. Calico’s rule for the primary network is “set the workload endpoint MTU and the tunnel MTUs to the same value (so all paths have the same MTU)”, with per-encapsulation subtractions of 20 for IP-in-IP, 50 for VXLAN IPv4 and 70 for VXLAN IPv6.[8] On a secondary interface the constraint is different in kind: a macvlan child’s mtu is capped at the master’s MTU, so you can never raise a pod above its host interface.[10] Rail traffic bypasses the primary CNI entirely, but the primary CNI still carries the NCCL bootstrap, so both budgets have to be right or you get a job that starts and then crawls.

9216 B
The three MTUs that must agree — switch port, host PF, pod interfaceSwitch portleaf port9216 BHost PFip link set <pf> mtu9216 BPod eth0Calico · VXLAN v49166 B50Pod net1rail0 … railN, VRF-isolated9216 B
1 warning
Pod eth0

Pod eth0 — 9166 B

921650 = 9166. network MTU − 50. Cilium documents the same 50 bytes per packet for VXLAN, which is why jumbo frames pay for themselves on either CNI.

Calico: You set it. Calico does not measure the fabric for you.

  • Rule, verbatim: “Set the workload endpoint MTU and the tunnel MTUs to the same value (so all paths have the same MTU).”
  • Manifest installs change it through the calico-config ConfigMap key veth_mtu, then the DaemonSet is restarted.
  • Verify the tunnel with ip link show; the IP-in-IP tunnel appears as tunlx.
  • Policy-only mode (CALICO_NETWORKING_BACKEND=none) is the clean answer when a different fabric owns routing — then there is no tunnel to budget for.

Workload MTU and tunnel MTU are both 9166 — Calico wants them identical so every path has the same MTU.

  • ⚠ Classic bug shape: a 9216 fabric with the pod network left at 1500. NCCL's TCP bootstrap still works and the data path crawls, so the ticket reads "training is slow", never "MTU is wrong".
  • Pod eth0 (9166) and pod net1 (9216) differ by 50 bytes. That is correct — the secondary path skips the tunnel — but net1 must still equal the PF and the switch port.
Set it and verify it
kubectl patch configmap/calico-config -n kube-system --type merge -p '{"data":{"veth_mtu": "9166"}}'
kubectl rollout restart daemonset/calico-node -n kube-system   # the doc says: restart the DaemonSet
ip link show   # the tunnel device MTU must read 9166
kubectl exec -it <pod> -- ip link   # eth0 must read 9166, net1 must read 9216
# SpectrumXRailPoolConfig:  railTopology: [{name: rail0, nicSelector: {pfNames: ["net_rail0"]}, cidrPoolRef: rail-0, mtu: 9216}]
kubectl -n nvidia-network-operator get spectrumxrailpoolconfig <name> -o jsonpath='{.status.syncStatus}'
Set the fabric to 9216 and compare the pod MTU the primary CNI ends up with against the 9216 the rail carries. Predict each number before revealing it.

5Verification, and the two failures you manufacture yourself

The operator gives you an ordered set of checks, and the order matters because each one depends on the previous.[4]

# 1. did the profile load on this node?
kubectl -n nvidia-network-operator logs -l app.kubernetes.io/name=nic-configuration-daemon \
  | grep "Loaded Spectrum-X profile"

# 2. what does the NIC think happened?
kubectl -n nvidia-network-operator get nicdevices
kubectl -n nvidia-network-operator get nicdevice <name> -o jsonpath='{.status.conditions}' | jq

# 3. did the rail pool sync?
kubectl -n nvidia-network-operator get spectrumxrailpoolconfig <name> \
  -o jsonpath='{.status.syncStatus}'
kubectl -n nvidia-network-operator get spectrumxrailpoolconfig <name> \
  -o jsonpath='{.status.nodeStates}' | jq

# 4. does a pod see the rail as an RDMA device?
kubectl exec -it spectrum-x-test -- rdma link
kubectl exec -it spectrum-x-test -- ip -6 route show vrf rail0

# 5. traffic, inside the VRF
ip vrf exec rail0 ib_write_bw -R ...

syncStatus takes Unknown, InProgress, Failed or Succeeded, with per-node detail in .status.nodeStates.[4] The NIC device condition reasons are the vocabulary for step 2: UpdateSuccessful is the target; UpdateStarted, PendingReboot, PendingNodeMaintenance and PendingFirmwareUpdate are in-progress states; PartiallyApplied means some parameters were unsupported and the message lists which; IncorrectSpec and SpecValidationFailed mean the template is invalid for that device; NonVolatileConfigUpdateFailed, RuntimeConfigUpdateFailed and FirmwareError mean the apply itself failed.[4]

Two failures on this page are ones the operator did not cause. The first is ip vrf exec: “RDMA-CM is not VRF-aware. Traffic tests that use RDMA-CM (for example ib_write_bw -R) must be run inside the VRF context.”[4] Forget it and you get a benchmark that connects to nothing on a fabric that is working. The second is capabilities: the quick start’s spectrum-x-test pod carries IPC_LOCK and NET_RAW, while the verify page says workloads that need ip vrf exec require IPC_LOCK, NET_ADMIN, DAC_OVERRIDE and BPF.[2][4] These are not contradictory pages, they are two different pods: start from the narrower set for a plain RDMA workload and widen only when the workload enters a VRF itself.

Two more rows close the table. Configuration that flaps means the SR-IOV Operator’s mellanox plugin is still enabled and fighting the NIC Configuration Operator — set disablePlugins: [mellanox].[4][2] A missing OVS bridge means the manageSoftwareBridges feature gate was not set on the SR-IOV Operator.[4][2] And OVS uplinks missing after a reboot is documented as “expected on Hardware Multiplane” with the evidence in /var/log/xplane-ovs-pre.log.[4]

Once the plumbing verifies, NCCL is the measurement layer. The rail CNI’s rdmaQoS sets tos: 96 and tc: 96;[3] NCCL exposes the same lever as NCCL_IB_TC, default 0, and NCCL_IB_GID_INDEX, default -1.[9] Teach these as one setting seen from two sides: a runbook that hardcodes a traffic class in the job while the CNI sets another one is a contradiction that no single page will report.

A rail pool that reports syncStatus Failed

A two-rail node, multiplaneMode: none, reports syncStatus: Failed on spectrumxrailpoolconfig rails.

  1. Read the per-node detail, because syncStatus is an aggregate: kubectl -n nvidia-network-operator get spectrumxrailpoolconfig rails -o jsonpath='{.status.nodeStates}' | jq.[4] One node is failing, the other is Succeeded — so this is node-scoped, not a bad CR.
  2. Check the NIC on that node: kubectl -n nvidia-network-operator get nicdevice <name> -o jsonpath='{.status.conditions}' | jq returns reason PartiallyApplied.[4]
  3. PartiallyApplied is documented as “some parameters unsupported” for that device ID or multiplane mode, and the message lists the skipped items.[4] So the profile loaded but does not cover this device.
  4. Confirm which profile loaded: kubectl -n nvidia-network-operator logs -l app.kubernetes.io/name=nic-configuration-daemon --field-selector spec.nodeName=<node> | grep "Loaded Spectrum-X profile".[4]
  5. Diagnosis: the failing node carries a different NIC generation from the others, and the profile ConfigMap has no mlxConfig entry for its device ID.[4] The fix is a profile that covers that device — not a change to the rail pool CR, which is correct.

The reasoning shape to keep: aggregate status tells you that something failed; nodeStates tells you where; the nicdevice condition tells you what; the daemon log tells you with which inputs.

Case notes, day one

How it ended

The same command with four words in front of it saturates the rail. Nothing about the cluster changed; the test finally ran where the traffic lives.[4] The network lead writes the prefix in his notebook and underlines it twice; the SE unmutes to ask whether row 14 can go green.

Then he asks the question that costs you the evening. Two BlueField-3 cards are sitting on the customer’s loading dock, ordered before anyone wrote a design, and the platform architect wants to know whether the operator that just fixed the rails can manage those too — with the customer’s own firewall running on the cards.

Lab

Optional, and only at a customer site with a real Spectrum-X cluster. Nothing in this section runs on the Dell-lab BlueField-3 host alone. Everything here is read-only: no CR is created, edited or deleted.

  1. Pre-flight inventory. Record kubectl version, helm list -A, kubectl get nicclusterpolicy nic-cluster-policy -o yaml and the operator version before touching anything. You are a guest on this cluster.
  2. kubectl -n nvidia-network-operator get nicdevices. Expected: one row per discovered NIC. If empty: the NIC Configuration Operator is not running or its node selector excludes these nodes.
  3. kubectl -n nvidia-network-operator logs -l app.kubernetes.io/name=nic-configuration-daemon | grep "Loaded Spectrum-X profile". Expected: one line per node naming the profile. If absent: go to the labelled-ConfigMap listing before anything else.
  4. kubectl -n nvidia-network-operator get spectrumxrailpoolconfig <name> -o jsonpath='{.status.syncStatus}' and then .status.nodeStates. Expected: Succeeded. If not: read the per-node detail, then the nicdevice conditions.
  5. kubectl -n nvidia-network-operator get ds xplane-ds and kubectl -n nvidia-network-operator logs -l app=xplane. Expected on hwplb: a DaemonSet with one ready pod per rail node. Expected on none or swplb: no such DaemonSet, which is correct, not a fault.
  6. kubectl -n nvidia-network-operator exec ds/xplane-ds -- doca-xplane-client get-status and ... get-planes-summary; for degradation, get-plane-failures-local --plane_id 0, get-plane-failures-remote --plane_id 0 and get-plane-traffic-summary --plane_id 0. Expected: all planes present and healthy.
  7. kubectl exec -it spectrum-x-test -- rdma link and kubectl exec -it spectrum-x-test -- ip -6 route show vrf rail0. Expected: one RDMA device named rdma_<rail> per rail, and routes inside the VRF.
  8. Rollback: none is required because no step mutates state. If you were asked to run a traffic test, remember it is a workload, not a configuration change — run it as ip vrf exec rail0 ib_write_bw -R ... and delete the test pod when you are done.

Retrieval check

10 questions from memory. Answer before looking anything up; misses become flashcards.

Explain it to a Dell SE

Explain to a Dell SE, in four or five sentences, what the Spectrum-X Operator adds that the Network Operator alone does not, and why a rail can be perfectly configured and still fail a bandwidth test.

14 flashcards for this lesson — 0 in deck. Spaced review lives at /review.

Sources

Facts in this lesson were checked against NVIDIA Network Operator v26.7.0 doc set (release notes, platform support, Spectrum-X overview / quick start / components / verify-and-troubleshoot), re-fetched 2026-09-07. Dates are when each page was fetched.

  1. NVIDIA Network Operator v26.7.0 — NVIDIA Spectrum-X · fetched 2026-09-07
  2. NVIDIA Network Operator v26.7.0 — Spectrum-X Quick Start · fetched 2026-09-07
  3. NVIDIA Network Operator v26.7.0 — Spectrum-X Architecture and Components · fetched 2026-09-07
  4. NVIDIA Network Operator v26.7.0 — Spectrum-X Verify and Troubleshoot · fetched 2026-09-07
  5. NVIDIA Network Operator v26.7.0 — Release Notes · fetched 2026-09-07
  6. NVIDIA Network Operator v26.7.0 — Platform Support · fetched 2026-09-07
  7. NVIDIA Network Operator v26.7.0 — NIC Configuration Operator CRD API Reference · fetched 2026-09-07
  8. Calico — Configure MTU to maximize network performance · fetched 2026-09-07
  9. NCCL User Guide — Environment Variables · fetched 2026-09-07
  10. CNI plugins — macvlan · fetched 2026-09-07

The same idea elsewhere

Other lessons that cover this ground, sometimes from another course's angle.