Dynamo Interconnect Check
OpenAIOfficialCreated by Dan GilDistributed by OpenAI
- Skill
- Review and audit
- Cloud and DevOps
- Apache-2.0
- Medium risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Dynamo Interconnect Check is a read-only diagnostic tool designed to verify that the transport layer for disaggregated serving is correctly configured. It ensures that NIXL, UCX, and NCCL are properly utilizing RDMA and NVLink for KV cache and tensor exchanges. By inspecting environment variables, node capabilities, and NIXL reachability, it helps operators confirm that their multi-node or disaggregated deployments are performing optimally and not silently falling back to slower, non-optimized transport paths.
What it does
- Inspects recipe configuration for critical NIXL, UCX, and NCCL environment variables.
- Probes worker pods for InfiniBand, GPUDirect RDMA, GDRCopy, and NVLink capabilities.
- Validates NIXL reachability and surfaces instructions for pairwise transfer testing.
- Provides a clear verdict on whether the disaggregated transport is ready for production use.
- Identifies missing tools or misconfigured fabric settings that could degrade performance.
When to use it
- Immediately after deploying a disaggregated or multi-node recipe via recipe-runner.
- Before benchmarking disaggregated throughput or latency to ensure valid results.
- When a deployment is functional but exhibits slow performance or unexpected behavior.
- To verify transport configuration consistency across different worker nodes.
Advantages
- Prevents reliance on deployments that are silently using sub-optimal transport paths.
- Provides actionable feedback on missing environment variables or fabric issues.
- Non-intrusive, read-only diagnostic approach that does not mutate cluster state.
- Helps distinguish between model-level issues and underlying fabric misconfigurations.
Usage examples
- “Check the transport environment variables for my qwen3-coder-480b disagg recipe.”
- The agent runs the env check script against the specified recipe path to report on critical NIXL/UCX/NCCL variables.
- “Validate the fabric capabilities for worker pod qwen-worker-0 in the dynamo-demo namespace.”
- The agent probes the pod for RDMA, GPUDirect, and NVLink status, returning a readiness verdict.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/openai/dynamo-interconnect-check/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip dynamo-interconnect-check.zip -d .agents/skills
Documentation by Dan Gil
Dynamo Interconnect Check
<!-- SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: CC-BY-4.0 -->
Purpose
Confirm that the transport disaggregated serving depends on actually works. A deployment can pass an endpoint smoke test while disagg is silently wrong: if NIXL/UCX cannot reach the peer worker over RDMA or NVLink, KV transfer falls back to a slow or broken path. Catch that with read-only checks before trusting a disagg deployment or its benchmark numbers.
This skill is read-only. It never mutates the cluster and never prints secrets.
Prerequisites
Python 3.10+ on the operator machine.
kubectl execaccess to a worker pod in the target Dynamo deployment.Read access to the recipe directory (
recipes/<model>/<framework>/<mode>).For node-capability checks: tools like
ibstat,nvidia-smi,lsmodavailable in the worker pod image (missing tools are reported asskipped, not failures).
When To Use
After
dynamo-recipe-runnerdeploys a disagg or multi-node recipe.Before reporting disagg throughput/latency, so numbers reflect the real transport.
When agg works but disagg is slow, hangs, or returns wrong output and you suspect the fabric rather than the model.
For diagnosing pods that are already crashing or unschedulable, use
dynamo-troubleshoot first.
Instructions
1. Check Transport Env Vars On The Recipe
python3 scripts/check_interconnect.py env recipes/<model>/<framework>/<mode>Reports which NIXL/UCX/NCCL transport variables are set and flags
disagg-critical ones (e.g. UCX_TLS, UCX_NET_DEVICES, NCCL_IB_HCA) that are
absent. Missing here is only a warning — they may be baked into the image — so
confirm with the node and NIXL checks. See
references/interconnect-env-vars.md for what each variable does.
2. Check Node Capabilities
Locally on a GPU node, or inside a running worker pod:
python3 scripts/check_interconnect.py node \
--namespace "${NAMESPACE}" --pod <worker-pod>Probes (read-only) for: InfiniBand devices and Active links, GPUDirect RDMA
(nvidia_peermem), GDRCopy, and NVLink in the GPU topology. Missing tools are
reported as skipped, not failures.
3. Validate NIXL Reachability
python3 scripts/check_interconnect.py nixl \
--namespace "${NAMESPACE}" --pod <worker-pod>Looks for NIXL test tooling in the pod and surfaces the exact next step to run a pairwise prefill↔decode transfer test. A full cross-pod transfer test requires two scheduled GPU pods on the fabric.
Available Scripts
| Script | Purpose | Arguments |
|---|---|---|
scripts/check_interconnect.py env | Inspect NIXL/UCX/NCCL env vars on a recipe | positional recipe path |
scripts/check_interconnect.py node | Probe InfiniBand, GPUDirect RDMA, GDRCopy, NVLink on a node or pod | --namespace, --pod |
scripts/check_interconnect.py nixl | Surface NIXL transfer-test readiness for a pod | --namespace, --pod |
Invoke via the agentskills.io run_script() protocol:
run_script("scripts/check_interconnect.py", args=["env", "recipes/qwen3-coder-480b/sglang/disagg"])
run_script("scripts/check_interconnect.py", args=["node", "--namespace", "dynamo-demo", "--pod", "qwen-worker-0"])Examples
Verify a disagg recipe's transport env shape before deploy:
python3 scripts/check_interconnect.py env recipes/qwen3-coder-480b/sglang/disaggAfter deploy, validate a worker pod's fabric:
python3 scripts/check_interconnect.py node \
--namespace dynamo-demo --pod qwen-worker-0
python3 scripts/check_interconnect.py nixl \
--namespace dynamo-demo --pod qwen-worker-0Equivalent through the agent protocol:
run_script("scripts/check_interconnect.py", args=["nixl", "--namespace", "dynamo-demo", "--pod", "qwen-worker-0"])Output Contract
Each check returns ok / warn / fail / skipped with a one-line detail,
plus a rolled-up verdict on disagg transport readiness. Report:
transport env vars present vs. disagg-critical ones missing
RDMA / GPUDirect / NVLink capability status
whether NIXL reachability was validated, and the next command if not
a clear statement of whether disagg can be trusted, or what to fix first
Limitations
Read-only fabric probe; does not run a full pairwise NIXL transfer (requires two scheduled GPU pods and the in-pod NIXL test tools).
skippedresults for missing tools (ibstat,nvidia-smi,lsmod) are inconclusive, not a pass.Env-var check inspects the recipe text; values injected at runtime via initContainers or operator-applied envs are not detected.
Single-node agg deployments do not exercise the transport — this skill is for disagg / multi-node validation.
Troubleshooting
| Symptom | Likely cause | Next step |
|---|---|---|
env reports all critical vars missing | Vars baked into image or injected by operator | Run the node check inside the worker pod to verify actual env |
node reports no Active IB link | Fabric down or HCA not provisioned to the node | Contact cluster admin; verify kubectl describe node shows nvidia.com/gpu and IB labels |
nvidia_peermem missing | GPUDirect RDMA module not loaded | Ask cluster admin to load nvidia-peermem; without it, NIXL falls back to staged copies |
nixl finds no test tools | Worker image lacks NIXL test harness | Use a NIXL-enabled image or run the standalone transfer test from a debug pod |
Benchmark
See BENCHMARK.md for the NVCARPS-EVAL performance report (auto-generated by the NVSkills CI pipeline). To refresh, re-run /nvskills-ci on an upstream PR touching this skill.
References
references/interconnect-env-vars.md— NIXL/UCX/NCCL env var catalog and IB capability checklist.Use
scripts/check_interconnect.pyfor all read-only checks.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.0 KiB
- BENCHMARK.md2.9 KiB
- LICENSEAdded by AutoGineer10.0 KiB
- SKILL.md6.3 KiB
- agents/openai.yaml365 B
- evals/evals.json3.1 KiB
- references/interconnect-env-vars.md3.5 KiB
- scripts/check_interconnect.pyScript12.8 KiB
- skill-card.md2.5 KiB
- skill.oms.sig5.0 KiB
scripts/check_interconnect.py
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Read-only checks that a Dynamo deployment's interconnect is disagg-ready.
A deployment can come up and answer ``/v1/models`` while disaggregated serving
is silently wrong, because nothing has exercised the NIXL/UCX transport that
moves KV cache between prefill and decode workers. This tool inspects the three
things that decide whether that transport will actually work:
* ``env`` - the NIXL/UCX/NCCL transport env vars set on a recipe or pod
* ``node`` - host/pod RDMA + GPUDirect + NVLink capabilities (read-only)
* ``nixl`` - a best-effort NIXL reachability probe between two pods
Everything is read-only and degrades gracefully when a tool, pod, or cluster is
not available, emitting structured JSON instead of crashing.
"""
from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any
# Tunables and conventional return codes (kept here to avoid magic numbers).
DEFAULT_PROBE_TIMEOUT_SEC = 20
# POSIX-conventional return codes used when the wrapper itself fails before
# the probed binary can produce a real one.
RETURNCODE_COMMAND_NOT_FOUND = 127 # binary not found in PATH or pod
# Transport-relevant env vars, grouped by subsystem. ``disagg`` marks the ones
# whose absence most often makes multi-node disaggregated serving fall back to a
# slow or incorrect transport. Names are distinctive enough to match anywhere in
# a manifest without false positives.
ENV_CATALOG: dict[str, dict[str, str]] = {
"UCX_TLS": {
"group": "nixl/ucx",
"disagg": "yes",
"why": "Selects UCX transports; must include rc/ib and cuda_ipc for "
"RDMA + NVLink, or NIXL silently falls back to TCP.",
},
"UCX_NET_DEVICES": {
"group": "nixl/ucx",
"disagg": "yes",
"why": "Pins UCX to the right IB HCA/port (e.g. mlx5_0:1); wrong or "
"unset device degrades to the management NIC.",
},
"UCX_IB_GPU_DIRECT_RDMA": {
"group": "nixl/ucx",
"disagg": "yes",
"why": "Enables GPUDirect RDMA so KV moves NIC<->GPU without staging "
"through host memory.",
},
"UCX_RNDV_SCHEME": {
"group": "nixl/ucx",
"disagg": "no",
"why": "Rendezvous scheme tuning for large transfers.",
},
"NIXL_PLUGIN_DIR": {
"group": "nixl/ucx",
"disagg": "no",
"why": "Where NIXL loads backend plugins from; only needed for a "
"non-default install layout.",
},
"NCCL_IB_HCA": {
"group": "nccl",
"disagg": "yes",
"why": "IB HCAs NCCL may use for tensor/expert parallel collectives.",
},
"NCCL_SOCKET_IFNAME": {
"group": "nccl",
"disagg": "yes",
"why": "Control-plane NIC for NCCL bootstrap; a wrong guess stalls "
"rendezvous.",
},
"NCCL_IB_DISABLE": {
"group": "nccl",
"disagg": "yes",
"why": "Must be 0/unset to use InfiniBand; =1 forces NCCL onto sockets.",
},
"NCCL_NET_GDR_LEVEL": {
"group": "nccl",
"disagg": "no",
"why": "GPUDirect RDMA aggressiveness for NCCL.",
},
"NCCL_P2P_LEVEL": {
"group": "nccl",
"disagg": "no",
"why": "NVLink/PCIe peer-to-peer level for intra-node collectives.",
},
"NCCL_IB_GID_INDEX": {
"group": "nccl",
"disagg": "no",
"why": "RoCE/EFA GID index; needed on RoCE fabrics, not classic IB.",
},
}
@dataclass
class Check:
"""One read-only probe result.
``status`` is one of ok / warn / fail / skipped / unknown so callers can
triage without parsing free text.
"""
name: str
status: str
detail: str
def run(cmd: list[str], timeout: int = DEFAULT_PROBE_TIMEOUT_SEC) -> dict[str, Any]:
"""Run a command read-only, never raising on failure or a missing binary."""
try:
proc = subprocess.run(
cmd, text=True, capture_output=True, timeout=timeout, check=False
)
return {"rc": proc.returncode, "out": proc.stdout, "err": proc.stderr}
except FileNotFoundError as exc:
return {"rc": 127, "out": "", "err": str(exc)}
except subprocess.TimeoutExpired:
return {"rc": 124, "out": "", "err": f"timed out after {timeout}s"}
def exec_prefix(namespace: str | None, pod: str, container: str | None) -> list[str]:
"""Build the ``kubectl exec`` prefix for running a probe inside a pod."""
cmd = ["kubectl", "exec", pod]
if namespace:
cmd += ["-n", namespace]
if container:
cmd += ["-c", container]
return cmd + ["--"]
def read_text(path: Path) -> str:
"""Read a manifest as text, tolerating undecodable bytes."""
try:
return path.read_text(encoding="utf-8")
except UnicodeDecodeError:
return path.read_text(errors="replace")
def find_env_value(text: str, var: str) -> str | None:
"""Return the value set for ``var`` in a manifest, or None if only named.
Handles both the Kubernetes ``- name: VAR\\n value: V`` shape and a plain
``VAR=V`` shape. Returns "" when the var is present but the value is dynamic
(valueFrom / secretKeyRef) or not on the same line.
"""
kv = re.search(rf"(?m)^\s*-?\s*name:\s*{re.escape(var)}\s*$", text)
if kv:
tail = text[kv.end() : kv.end() + 200]
val = re.search(r"^\s*value:\s*[\"']?([^\"'\n]+)", tail)
return val.group(1).strip() if val else ""
inline = re.search(rf"(?m)\b{re.escape(var)}=([^\s\"']+)", text)
if inline:
return inline.group(1)
return None
def check_env(target: Path) -> list[Check]:
"""Assess transport env vars across one manifest or a recipe directory."""
if target.is_dir():
files = sorted(target.rglob("*.yaml")) + sorted(target.rglob("*.yml"))
elif target.is_file():
files = [target]
else:
return [Check("env", "fail", f"no manifest found at {target}")]
if not files:
return [Check("env", "fail", f"no YAML manifests under {target}")]
text = "\n".join(read_text(f) for f in files)
checks: list[Check] = []
missing_disagg: list[str] = []
for var, meta in ENV_CATALOG.items():
value = find_env_value(text, var)
if value is None:
if meta["disagg"] == "yes":
missing_disagg.append(var)
continue
shown = value if value else "<set via valueFrom/dynamic>"
checks.append(Check(f"env:{var}", "ok", f"{shown} ({meta['why']})"))
if missing_disagg:
checks.append(
Check(
"env:disagg-transport",
"warn",
"disagg-critical vars not set in manifest: "
+ ", ".join(missing_disagg)
+ ". Fine if baked into the image/entrypoint; verify with the "
"`node` and `nixl` checks. See references/interconnect-env-vars.md.",
)
)
if not checks:
checks.append(
Check(
"env",
"warn",
"no NIXL/UCX/NCCL transport env vars found in the manifest(s)",
)
)
return checks
# Read-only capability probes: (check name, argv, how to read the result).
NODE_PROBES: list[tuple[str, list[str]]] = [
("ib-devices", ["ls", "/dev/infiniband"]),
("ibv-devinfo", ["ibv_devinfo", "-l"]),
("ib-link", ["ibstat"]),
("gpudirect-peermem", ["sh", "-c", "lsmod | grep -E 'nvidia_peermem|nv_peer_mem'"]),
("gdrcopy", ["ls", "/dev/gdrdrv"]),
("gpu-topology", ["nvidia-smi", "topo", "-m"]),
]
def classify_node_probe(name: str, res: dict[str, Any]) -> Check:
"""Turn a raw probe result into a triaged Check."""
out = (res["out"] or "").strip()
if res["rc"] == RETURNCODE_COMMAND_NOT_FOUND:
return Check(name, "skipped", "tool/path not present in this environment")
if res["rc"] != 0:
return Check(name, "warn", (res["err"] or "non-zero exit").strip()[:200])
if name == "ib-link":
state = "ok" if re.search(r"State:\s*Active|LinkUp", out) else "warn"
detail = "at least one port Active" if state == "ok" else "no Active IB port"
return Check(name, state, detail)
if name == "gpu-topology":
link = "ok" if re.search(r"\bNV\d+\b", out) else "warn"
detail = "NVLink (NV#) present" if link == "ok" else "no NVLink links in topo"
return Check(name, link, detail)
if name in {"ib-devices", "ibv-devinfo"}:
return Check(name, "ok" if out else "warn", out[:200] or "no devices listed")
if name == "gpudirect-peermem":
return Check(name, "ok" if out else "warn", out[:120] or "module not loaded")
if name == "gdrcopy":
return Check(name, "ok", out[:120])
return Check(name, "ok", out[:200])
def check_node(
namespace: str | None, pod: str | None, container: str | None
) -> list[Check]:
"""Run RDMA/GPUDirect/NVLink capability probes locally or inside a pod."""
prefix = exec_prefix(namespace, pod, container) if pod else []
where = f"pod {pod}" if pod else "local host"
checks = [Check("node:target", "ok", where)]
for name, argv in NODE_PROBES:
checks.append(classify_node_probe(name, run(prefix + argv)))
return checks
def check_nixl(
namespace: str | None, pod: str | None, container: str | None
) -> list[Check]:
"""Best-effort NIXL transport probe; needs a real multi-pod GPU/IB cluster.
Looks for a NIXL test/bench binary inside the pod and reports how to run a
pairwise RDMA/NVLink check. A correct cross-pod transfer test cannot be
synthesized without two scheduled GPU pods on the fabric, so this surfaces
the exact next command rather than asserting a false pass.
"""
if not pod:
return [
Check(
"nixl",
"skipped",
"pass --pod (and --namespace) to probe NIXL inside a worker; a "
"real prefill<->decode transfer test needs two GPU pods on the "
"fabric. See references/interconnect-env-vars.md.",
)
]
prefix = exec_prefix(namespace, pod, container)
probe = run(
prefix
+ [
"sh",
"-c",
"command -v nixlbench nixl_test 2>/dev/null; ls /usr/local/nixl 2>/dev/null",
]
)
found = (probe["out"] or "").strip()
if probe["rc"] == RETURNCODE_COMMAND_NOT_FOUND or not found:
return [
Check(
"nixl:binary",
"warn",
"no nixlbench/nixl_test found in the pod; install or exec the "
"NIXL test harness to validate RDMA/NVLink reachability between "
"a prefill and a decode pod.",
)
]
return [
Check(
"nixl:binary",
"ok",
f"found NIXL tooling: {found}. Run a pairwise transfer between a "
"prefill and decode pod to confirm the transport (see references).",
)
]
def summarize(checks: list[Check]) -> dict[str, Any]:
"""Roll the checks up into a verdict on disagg transport readiness."""
counts = {s: sum(c.status == s for c in checks) for s in ("ok", "warn", "fail")}
if counts["fail"]:
verdict = "transport blockers found; disagg will not be correct"
elif counts["warn"]:
verdict = "potential transport gaps; verify before trusting disagg"
else:
verdict = "no transport gaps detected by read-only checks"
return {
"checks": [asdict(c) for c in checks],
"counts": counts,
"verdict": verdict,
}
def main() -> int:
"""CLI entry point. Returns 0 on no failures, 1 when any check failed."""
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest="command", required=True)
env_p = sub.add_parser("env", help="Assess transport env vars in a recipe/manifest")
env_p.add_argument("target", help="deploy.yaml or recipe directory")
for name, helptext in [
("node", "Check RDMA/GPUDirect/NVLink capabilities (local or in a pod)"),
("nixl", "Best-effort NIXL RDMA/NVLink reachability probe"),
]:
p = sub.add_parser(name, help=helptext)
p.add_argument("--namespace", "-n")
p.add_argument("--pod", help="Probe inside this pod via kubectl exec")
p.add_argument("--container", "-c")
args = parser.parse_args()
if args.command == "env":
checks = check_env(Path(args.target))
elif args.command == "node":
checks = check_node(args.namespace, args.pod, args.container)
else:
checks = check_nixl(args.namespace, args.pod, args.container)
result = summarize(checks)
print(json.dumps(result, indent=2))
return 1 if result["counts"]["fail"] else 0
if __name__ == "__main__":
sys.exit(main())
Requirements and compatibility
Written for: OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python, Needs Node.js
Security
What our scanner noticed· Medium risk
- Large base64-encoded blobs
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Dan Gil
- Publisher
- OpenAI (Official)
- Distributed by
- OpenAI
- Source
- openai/plugins · plugins/nvidia/skills/dynamo-interconnect-check · commit d420d13
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (written by AutoGineer)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Also distributed in
- NVIDIA/skills
AutoGineer is not affiliated with or endorsed by Dan Gil or OpenAI.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download dynamo-interconnect-check.zip