Reproducible network experiments with a single command, on a single machine — no manual dependency installation, no manual setup, no manual cleanup, repeat anywhere at any time with exactly the same binaries. Define nodes, links, and scripts with the Nix language.
Add nixnet as a flake input and call mkExperiment from legacyPackages:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
nixnet.url = "github:birneee/nixnet";
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" "aarch64-linux" ];
perSystem = { pkgs, inputs', ... }: {
packages.default = inputs'.nixnet.legacyPackages.mkExperiment {
nodePackages = with pkgs; [ coreutils iperf3 ];
nodes = {
client = {
networking.interfaces.eth0.ipv4.addresses = [{ address = "10.0.0.1"; prefixLength = 24; }];
scripts.main = { exec = "sleep 0.1; iperf3 -c 10.0.0.2"; await = true; };
};
server = {
networking.interfaces.eth0.ipv4.addresses = [{ address = "10.0.0.2"; prefixLength = 24; }];
scripts.main = { exec = "iperf3 -s"; };
};
};
veths.eth0 = {
netem.lossPercent = 1;
a.node = "client";
b.node = "server";
};
};
};
};
}Run with:
nix run
- Portable — runs on any Linux machine with Nix installed
- Declarative — nodes, bridges, links, routes, and scripts defined in Nix
- Reproducible — all binaries pinned via nixpkgs
- netem — delay, loss, rate limiting per link or endpoint
- Routing — static routes, default routes, IP forwarding
- ARP control — disable ARP or prefill tables with peer MACs
- Repeatable — repeat with
sudo nix run . 1-5;{run}inworkDirbecomes a run index - Foreground scripts — full terminal access for interactive tools
- Sandboxing — experiments are isolated with Linux namespaces to prevent side effects
- Host binds — expose host directories, files and binaries inside node namespaces via
hostBind - Automatic cleanup — namespaces, files and processes cleaned up on exit
- Mermaid diagrams — topology diagram from config
| nixnet | mininet | containerlab | manual scripts | |
|---|---|---|---|---|
| Config | Nix | Python | YAML | bash |
| Isolation | mnt, pid, net, ipc, uts, user namespaces | net namespaces | Docker | net namespaces |
| Reproducibility | ✓ | ✗ | partial | ✗ |
| Real network stack | ✓ | ✓ | ✓ | ✓ |
| Cleanup on exit | ✓ | ✓ | ✓ | manual |
| Runtime dependency | Nix | Python + OVS | Docker | iproute2 |
| Dependency management | nixpkgs | pip / manual | Docker images | manual |
| Visualization | mermaid diagram | ✓ | ✓ | ✗ |
nixnet is designed for lightweight, reproducible experiments that run real application binaries directly in network namespaces — no container overhead, no Python runtime, no daemon. Nix is the only runtime dependency; all other tools including iproute2 are fetched from nixpkgs. The output is a single self-contained shell script pinned to exact package versions via Nix.
Performance evaluation is available here.
See examples/ for inspiration. Examples can be run directly without cloning:
nix run 'github:birneee/nixnet?dir=examples/ping'Show mermaid graph:
xdg-open $(nix build 'github:birneee/nixnet?dir=examples/ping#mermaid-svg' --no-link --print-out-paths)The experiment testbed runs in two phases:
- Setup — creates a testbed namespace, within creates node namespaces, applies sysctl settings, creates veth pairs, assigns addresses, brings interfaces up, configures MTU, ARP, netem, and routes. Hook:
preSetup/postSetup. - Run — launches all background scripts in parallel, then foreground scripts sequentially. Waits for scripts with
await = truebefore exiting. Hook:preRun/postRun.
Cleanup (SIGINT to all child processes, namespace deletion) runs automatically on exit regardless of which phase it occurs in.
All hooks run as root — use with care.
- Root is not required for most features. Run with
nix runas a regular user. Some features require root: eBPF/XDP programs needCAP_NET_ADMINoutside a user namespace — usesudo nix runfor those. - Cleanup (SIGINT to all child processes, namespace deletion) happens automatically on exit.
- Use
await = trueon a background script to block the experiment testbed from exiting until that script finishes. - Use
foreground = truefor an interactive shell:{ exec = "bash"; foreground = true; }.
nix build .#nixnet-option-docs
cat resultnix build .#mermaid && cat resultOpen as SVG:
xdg-open $(nix build .#mermaid-svg --no-link --print-out-paths)Tip: run
nix store gcafterwards to clean up build artifacts.
For option documentation and completions with nixd, add the following to your nixd settings:
{
"options": {
"nixnet": {
"expr": "(builtins.getFlake (toString ./.)).inputs.nixnet.legacyPackages.x86_64-linux.options"
}
}
}Contributions are welcome! Feel free to open issues or pull requests.
MIT or Apache 2.0, at your option.