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.
-
Check if your machine meets the requirements: Installation
-
Add NixNet as a flake input and call
mkExperimentfromlegacyPackagesfor experiment:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
nixnet.url = "github:birneee/nixnet"; # <-- Adding NixNet here
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" "aarch64-linux" ];
perSystem = { pkgs, inputs', ... }: {
# EXPERIMENT: Define the virtual network lab
packages.default = inputs'.nixnet.legacyPackages.mkExperiment {
# Install standard tools and iperf3 on all virtual nodes
nodePackages = with pkgs; [ coreutils iperf3 ];
# TOPOLOGY: Define the nodes and what they do
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
nix run
NixNet is a regular Nix flake input, no manual installation required. To run NixNet your system requires:
- Linux supporting namespaces
- Namespace limit must be sufficient for the experiment
- Nix installation (link or alternatives)
- Nix experimental features enabled:
flakes,nix-command - AppArmor may need to be disabled
- 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.