netdryrun

Official registry Other

Dry-run network changes in a real containerlab lab: BGP, routes, reachability, with evidence.

Install

Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).

PYPI package netdryrun
Claude Code
claude mcp add netdryrun -- uvx netdryrun==0.3.0 mcp
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "netdryrun": {
            "command": "uvx",
            "args": [
                "netdryrun==0.3.0",
                "mcp"
            ]
        }
    }
}

Documentation

From the project's README on GitHub, refreshed weekly. Links open on GitHub.

Contents
  1. Why
  2. Quick start
  3. netdryrun.yml
  4. Commands
  5. GitHub Action
  6. For AI agents (MCP)
  7. How it compares
  8. Honest limits
  9. Roadmap
  10. License

Test network changes before they touch production.

Netdryrun™ boots your topology as a real virtual network (containerlab), applies your configs, and checks how the network actually behaves: BGP sessions, prefix counts, routes, reachability. It catches mistakes like route leaks before they reach production, and leaves evidence a reviewer can trust.

$ netdryrun run examples/frr-bgp-broken
Deploying lab ndr-frr-broken (3 nodes)...
  [PASS] r1: BGP session to 10.0.12.2 is established -- state is established (6.3s)
  [PASS] r1: ping 192.168.3.1 from 192.168.1.1 expects success -- 3/3 replies (2.3s)
  ... (3 more passing checks trimmed)
  [FAIL] r1: route 0.0.0.0/0 via bgp is absent -- unexpectedly present via bgp (not selected) (0.1s)
FAILED: 5 passed, 1 failed (6 checks)

That run caught a leaked default route that was received but not selected. A ping test or a config linter would have missed it.

Status: v0.3.0, early. Works today with FRR, Nokia SR Linux, SONiC (docker-sonic-vs) and Arista EOS (cEOS, bring your own image) on Linux and WSL2. On the verdict corpus (30 realistic BGP changes, expected verdicts committed before any run), Netdryrun judged 24/30 correctly, ping-only checks 20/30 and all-sessions-up checks 15/30. Feedback and issues are very welcome.

Why

  • It tests behavior, not syntax. Linters and config diffs can't tell you whether BGP will come up or whether a prefix leaks. A real control plane can.
  • It's built for CI. One GitHub Action, a pass/fail exit code, a Markdown summary in the job, JUnit XML, and a JSON evidence file with every command and raw output.
  • It doesn't lie about convergence. Positive checks wait for the network to converge. Negative checks ("this route must be absent") run only after convergence and must hold for a time window, so they can't pass just because BGP hasn't come up yet.

Quick start

Try it in your browser (no install)

The codespace comes with Docker, containerlab and Netdryrun already installed. When the terminal shows Ready, run:

bash examples/agent-demo/demo.sh

A route-leaking change is rejected, then the fix passes every check. It uses your GitHub account's free Codespaces hours (2-core machine). Details.

On your own machine

Requirements: Linux or WSL2, Docker, containerlab, Python 3.10+.

pip install netdryrun
git clone https://github.com/netdryrun/netdryrun && cd netdryrun
netdryrun run examples/frr-bgp          # 3 FRR routers, should pass
netdryrun run examples/srl-frr          # SR Linux + FRR, should pass
netdryrun run examples/frr-bgp-broken   # a bad change, should fail
On your own containerlab topology (3 commands)
netdryrun record my-lab/topology.clab.yml --deploy   # snapshot today's behavior into my-lab/netdryrun.yml
# ...edit your configs (the change you want to test)...
netdryrun run my-lab                                   # did anything break?

record writes a baseline of every established BGP session, prefix count and BGP route. Review it, commit it, and every future change is tested against it, locally or in CI.

netdryrun.yml

lab: topology.clab.yml      # your containerlab topology
timeout: 90                 # seconds a positive check may wait for convergence

checks:
  - type: bgp_session       # session reaches a state (default: established)
    node: r1
    neighbor: 10.0.12.2
  - type: bgp_prefixes      # accepted prefix count within a range
    node: r1
    neighbor: 10.0.12.2
    min: 1
    max: 10
  - type: route             # route present (or absent), optionally via a protocol
    node: r1
    prefix: 192.168.3.0/24
    protocol: bgp
  - type: ping              # reachability, optionally from a source address
    node: r1
    source: 192.168.1.1
    target: 192.168.3.1
  - type: route             # negative check: must stay absent after convergence
    node: r1
    prefix: 0.0.0.0/0
    protocol: bgp
    present: false

Failure scenarios break something, check the network still behaves, then restore it. They can take links down, or make an FRR neighbor announce routes it shouldn't, to prove a max-prefix limit or an import filter actually holds:

scenarios:
  - name: r1-r2 link fails
    link_down: {node: r1, interface: eth1}
    checks:
      - {type: ping, node: r1, source: 192.168.1.1, target: 192.168.2.1}   # rerouted via r3
      - {type: bgp_session, node: r1, neighbor: 10.0.12.2, state: down}
  - name: customer floods 200 prefixes
    inject_routes: {node: ce1, count: 200, from: 10.100.16.0/24}
    checks:
      - {type: bgp_prefixes, node: up1, neighbor: 10.0.2.1, max: 51}   # the limit stops the flood

Every check also accepts name, timeout and vrf. Ping accepts expect: failure and count. Drivers are detected from the topology; override them with nodes: {r1: {driver: frr}}. Nodes that are configured after boot (SONiC) take setup commands: nodes: {sonic1: {setup: [...]}}.

CheckFRRSR LinuxArista EOS (cEOS)SONiC (VS)
bgp_session✅✅✅✅
bgp_prefixes (accepted = received minus rejected by policy)✅✅✅✅
route present / absent✅✅✅✅
bgp_route best-path attributes: communities (standard, large, well-known), AS path (exact or regex), local preference, MED, next hop✅✅✅✅
ping success / failure✅✅✅✅

Commands

CommandWhat it does
netdryrun run [path]Deploy the lab, run the checks, destroy the lab. Add --keep to leave it up for debugging
netdryrun check [path]Run the checks against a lab that is already running
netdryrun validate [path]Validate netdryrun.yml without running anything
netdryrun record topology.clab.ymlSnapshot a known-good lab into a netdryrun.yml baseline, so you don't write checks by hand

Reports: --json FILE (full evidence), --markdown FILE, --junit FILE. Under GitHub Actions, a summary is appended to the job page automatically. Exit codes: 0 passed, 1 a check failed, 2 config or lab error, 3 inconclusive (the lab never reached a testable state, so the run says nothing about the change).

GitHub Action

name: Network dry run
on: pull_request
jobs:
  dryrun:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: netdryrun/netdryrun@v0.3.0
        with:
          path: network/        # folder containing netdryrun.yml

The action installs containerlab, runs the checks, uploads the JSON and JUnit reports as an artifact, and fails the job if any check fails. Outputs: status, passed, failed, inconclusive-reason.

For AI agents (MCP)

AI can propose network changes. Netdryrun verifies the outcome. Agents: read AGENTS.md.

AI agents are starting to change networks. Netdryrun gives them a safety gate: an MCP server that lets an agent dry-run its change in a real lab and proceed only if every check passes.

pip install netdryrun

Add it to your MCP client (for example Claude Code):

claude mcp add netdryrun -- netdryrun mcp

Tools: dry_run (deploy, check, destroy, verdict), run_checks (against a running lab), validate, check_types. Each result carries a separate verdict field: SAFE TO PROCEED, DO NOT APPLY THIS CHANGE, or INCONCLUSIVE: the lab did not reach a testable state; ... when the lab never came up.

Demo (examples/agent-demo): the ticket says the customer's new block must reach the upstream and the peer. The tempting change passes every ticket check, session and ping, but leaks routes; Netdryrun rejects it. The two-line fix passes the same checks. (A scripted replay of the two attempts; CI runs it.)

Safety model:

  • The server never touches production. It has no tool that pushes config anywhere; it only builds and tests labs.
  • Tool results contain normalized check results, not raw device output, so text inside a lab (a banner, a route description) can't inject instructions into the agent. Raw evidence goes to the JSON report file.
  • A lab is code: containerlab runs privileged containers. Only point the server at topologies you trust, on a machine you'd let run Docker.

How it compares

Netdryrun complements these tools; it doesn't replace them.

ToolWhat it does bestWhere Netdryrun fits
BatfishStatic analysis of configs, without booting anything; fast and broadNetdryrun runs the real control plane, so it sees runtime behavior: convergence, timers, what actually lands in the RIB
netlabBuilds labs from a high-level topology, with built-in validationUse netlab to build the lab and Netdryrun for convergence-aware CI gates and evidence reports
pyATS / GenieA full test framework with deep Cisco parsersNetdryrun is a small declarative YAML file plus one GitHub Action, with no test code to write
containerlabRuns the labNetdryrun drives containerlab; it doesn't replace it

What Netdryrun adds: positive checks wait for convergence, negative checks run only after convergence and must hold, received-but-not-selected routes count as present, and every run produces one pass/fail plus an evidence file.

Honest limits

  • Lab routers are not your production routers. A pass on FRR, SR Linux, SONiC VS or Arista cEOS is strong evidence for protocol logic and policy intent, but not proof of hardware, scale or version-specific behavior. For Arista, bring your own licensed cEOS image to test real vendor code.
  • Checks cover IPv4/IPv6 unicast BGP (sessions, prefix counts, best-path attributes), routes and ping, with link-down, node-isolation and route-injection (FRR only) failure scenarios. More checks are coming: OSPF/IS-IS adjacencies, path/traceroute.

How results are checked, and who writes the code (mostly AI agents): How it's built.

Roadmap

  • Outage replays: famous real-world outages (route leaks, mass withdrawals) rebuilt as runnable labs and check packs. Would your change have caused one?
  • More failure scenarios, more check types (OSPF/IS-IS, traceroute) and drivers
  • PR comments with diffs, and signed evidence for change tickets

License

Apache-2.0. "Netdryrun" and the Netdryrun logo are trademarks; see TRADEMARKS.md.

Frequently asked questions

What is netdryrun?

Dry-run network changes in a real containerlab lab: BGP, routes, reachability, with evidence.

How do I install netdryrun?

It runs on your machine. In Claude Code: claude mcp add netdryrun -- uvx netdryrun==0.3.0 mcp. For other clients, paste the JSON configuration from this page into their MCP settings.

Does netdryrun need an API key?

Its registry entry lists no required key or secret. Some features may still need an account with the service it connects to.

Is netdryrun open source?

Yes. Its code is on GitHub under the Apache-2.0 license.

Who maintains netdryrun?

It is published by netdryrun. AI.tn lists it but does not maintain or endorse it.

From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.