Skip to main content

GitHub Repo stars GitHub Downloads GitHub Release GitHub Issues GitHub Pull Requests


❓ What is this?

Helm chart for automating the deployment of virtual network topologies on Kubernetes using Pods with multiple interfaces. It leverages the Multus CNI plugin and renders the required Kubernetes resources (e.g., ConfigMaps, Pods, NetworkAttachmentDefinitions) from a structured YAML-based topology definition.
Use it to quickly bring up containerized network labs for testing, automation, development, and education — all within your cluster.

⚙️ Use cases

This chart enables rapid deployment of containerized network topologies on Kubernetes. Key use cases include:

  • Network design validation: Test high- and low-level design (HLD/LLD) configurations and device behavior before committing to a final design.
  • Test automation: Develop and verify automation scripts for traffic or protocol generators/analyzers (e.g., IxNetwork APIs, OTG) — effectively unit-testing your test logic.
  • Image validation: Validate new versions of network operating systems (NOS), whether virtual or hardware-aligned, to ensure feature support and functionality.
  • Training & certification prep: Practice CLI, protocols, and topologies in a safe, repeatable lab — ideal for students and professionals preparing for vendor certifications.

📦 Prerequisites

Before installing Netclab Chart, ensure the following are present:

⚡ Quick start: netclab

netclab, on PyPI, brings a lab up in one command: the kind cluster, the CNI plugins, Multus, a local registry, and this chart, from a topology file such as examples/topology-frrouting.yaml:

uvx netclab up --namespace dc2 --values topology-frrouting.yaml
uvx netclab down --namespace dc2    # this lab
uvx netclab down                    # the whole cluster; the registry and its images stay

cEOS cannot be pulled, so it goes into the registry once. The cluster finds ceos:<version> there before Docker Hub, and the topology names it in ceos.image:

docker import ./cEOS64-lab-<version>.tar.xz ceos:<version>
docker tag ceos:<version> localhost:5001/library/ceos:<version>
docker push localhost:5001/library/ceos:<version>
ceos:
  image: ceos:<version>

netclab works only on a cluster made with its kind.yaml, and refuses any other; netclab down removes it. It also refuses a lab whose nodes ask for more CPU or memory than the cluster has free. etcd keeps its data in memory, so the cluster does not survive a restart of Docker or of the host.

🚀 What netclab up does, by hand

netclab up runs these steps for you, and also sets up the local registry. They are here to show what it does, or to run a lab without it.

kind create cluster --name netclab --config kind.yaml
CNI=$(basename "$(curl -s -o /dev/null -w '%{redirect_url}' https://github.com/containernetworking/plugins/releases/latest)")
docker exec netclab-control-plane bash -c \
"curl -L https://github.com/containernetworking/plugins/releases/download/${CNI}/cni-plugins-linux-amd64-${CNI}.tgz \
| tar -xz -C /opt/cni/bin ./bridge ./host-device"
MULTUS=$(basename "$(curl -s -o /dev/null -w '%{redirect_url}' https://github.com/k8snetworkplumbingwg/multus-cni/releases/latest)")
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/${MULTUS}/deployments/multus-daemonset.yml
kubectl -n kube-system wait --for=jsonpath='{.status.numberReady}'=1 --timeout=5m daemonset.apps/kube-multus-ds
  • Add helm repo for netclab chart:
helm repo add netclab https://netclab.github.io/netclab-chart
helm repo update

🧩 Usage

After these steps, you can manage your topology using the YAML file. Pods will be created according to the topology definition.

Note:
Node and network names must use lowercase letters, digits, or hyphens.
Host interface names are limited to 15 characters.
Netclab computes veth names as <release>-<network>-<hash(node)>, automatically shortening long node names for Linux compatibility.
Recommendation: keep Helm release and network names short (≤ 3 characters).

Configuration options are documented in the table below. You can override these values in your own file.

Parameter Description Defaults
topology.networks.type Type of connection between nodes. Can be bridge or veth. veth
topology.nodes.type Type of node. Can be: srlinux, frrouting, ceos, linux
topology.nodes.image Container images used for topology nodes. ghcr.io/nokia/srlinux:26.7.2
quay.io/frrouting/frr:10.7.1
ceos: none, see ceos.image
bash:5.3.20
topology.nodes.memory Memory allocation per node type. srlinux: 4Gi
frr: 512Mi
ceos: 4Gi
linux: 200Mi
topology.nodes.cpu CPU allocation per node type. srlinux: 2000m
frr: 500m
ceos: 2000m
linux: 200m
ceos.image Image of every cEOS node that names none. Required when a cEOS node names none. none
ceos.restconfSslProfile SSL profile for cEOS RESTCONF on port 6020. ARISTA_DEFAULT_SELF_SIGNED_PROFILE

Note:
cEOS cannot be pulled, so the chart has no default image for it. Download the cEOS image from Arista Networks, import it into your cluster, and name it in ceos.image:

docker import ./cEOS64-lab-<version>.tar.xz ceos:<version>
kind load docker-image ceos:<version> -n netclab

After loading, you can verify the image with:

docker exec netclab-control-plane crictl images | grep ceos

🧱 Example topology

To make the topology and config files easy to reach:

git clone https://github.com/netclab/netclab-chart.git && cd netclab-chart
+--------+
| h01    |
|        |
|    e1  |
+--------+
    |
    b2
    |
+-----------+          +-----------+
| e1-2      |          | srl02 or  |
| or eth2   |          | frr02 or  |
|           |          | ceos02    |
|           |          |           |
|           |          |           |
|       e1-1| -- b1 -- | e1-1      |
|    or eth1|          | or eth1   |
|           |          |           |
|           |          |           |
| srl01 or  |          |           |
| frr01 or  |          |     e1-2  |
| ceos01    |          |  or eth2  |
+-----------+          +-----------+
                              |
                              b3
                              |
                         +--------+
                         |     e1 |
                         |        |
                         | h02    |
                         +--------+

Follow instructions for SRLinux or/and FRRouting or/and cEOS

Note: The topologies are independent and can run in separate Kubernetes namespaces.

SRLinux details
  • Start nodes:

    helm install dc1 netclab/netclab --values ./examples/topology-srlinux.yaml
    
    kubectl get pod
    
    NAME                 READY   STATUS    RESTARTS   AGE
    h01                  1/1     Running   0          12s
    h02                  1/1     Running   0          12s
    srl01                1/1     Running   0          12s
    srl02                1/1     Running   0          12s
    
  • Configure nodes (repeat if they're not ready yet):

    kubectl exec h01 -- ip address replace 172.20.0.2/24 dev e1
    kubectl exec h01 -- ip route replace 172.30.0.0/24 via 172.20.0.1
    
    kubectl exec h02 -- ip address replace 172.30.0.2/24 dev e1
    kubectl exec h02 -- ip route replace 172.20.0.0/24 via 172.30.0.1
    
    kubectl cp ./examples/srl01.cfg srl01:/srl01.cfg
    kubectl exec srl01 -- bash -c 'sr_cli --candidate-mode --commit-at-end < /srl01.cfg'
    
    kubectl cp ./examples/srl02.cfg srl02:/srl02.cfg
    kubectl exec srl02 -- bash -c 'sr_cli --candidate-mode --commit-at-end < /srl02.cfg'
    
    All changes have been committed. Leaving candidate mode.
    All changes have been committed. Leaving candidate mode.
    
  • Test (convergence may take time):

    kubectl exec h01 -- ping 172.30.0.2 -I 172.20.0.2
    
  • LLDP neighbor information:

    kubectl exec srl01 -- sr_cli show system lldp neighbor
    
    +--------------+-------------------+----------------------+---------------------+------------------------+----------------------+---------------+
    |     Name     |     Neighbor      | Neighbor System Name | Neighbor Chassis ID | Neighbor First Message | Neighbor Last Update | Neighbor Port |
    +==============+===================+======================+=====================+========================+======================+===============+
    | ethernet-1/1 | 00:01:03:FF:00:00 | srl02                | 00:01:03:FF:00:00   | 47 seconds ago         | 24 seconds ago       | ethernet-1/1  |
    +--------------+-------------------+----------------------+---------------------+------------------------+----------------------+---------------+
    
FRRouting details
  • Start nodes:

    helm install dc2 netclab/netclab --values examples/topology-frrouting.yaml  --namespace dc2 --create-namespace
    kubectl config set-context --current --namespace dc2
    
    kubectl get pod
    
    NAME               READY   STATUS    RESTARTS   AGE
    frr01              1/1     Running   0          6s
    frr02              1/1     Running   0          6s
    h01                1/1     Running   0          6s
    h02                1/1     Running   0          6s
    
  • Configure nodes (repeat if they're not ready yet):

    kubectl exec h01 -- ip address replace 172.20.0.2/24 dev e1
    kubectl exec h01 -- ip route replace 172.30.0.0/24 via 172.20.0.1
    
    kubectl exec h02 -- ip address replace 172.30.0.2/24 dev e1
    kubectl exec h02 -- ip route replace 172.20.0.0/24 via 172.30.0.1
    
    kubectl exec frr01 -- ip address add 10.0.0.1/32 dev lo
    kubectl exec frr01 -- ip address replace 10.0.1.1/24 dev e1-1
    kubectl exec frr01 -- ip address replace 172.20.0.1/24 dev e1-2
    kubectl exec frr01 -- touch /etc/frr/vtysh.conf
    kubectl exec frr01 -- sed -i -e 's/bgpd=no/bgpd=yes/g' /etc/frr/daemons
    kubectl exec frr01 -- sh -c '/usr/lib/frr/frrinit.sh start > /var/log/frrinit.log 2>&1; tail -1 /var/log/frrinit.log'
    kubectl cp ./examples/frr01.cfg frr01:/frr01.cfg
    kubectl exec frr01 -- vtysh -f /frr01.cfg
    
    kubectl exec frr02 -- ip address add 10.0.0.2/32 dev lo
    kubectl exec frr02 -- ip address replace 10.0.1.2/24 dev e1-1
    kubectl exec frr02 -- ip address replace 172.30.0.1/24 dev e1-2
    kubectl exec frr02 -- touch /etc/frr/vtysh.conf
    kubectl exec frr02 -- sed -i -e 's/bgpd=no/bgpd=yes/g' /etc/frr/daemons
    kubectl exec frr02 -- sh -c '/usr/lib/frr/frrinit.sh start > /var/log/frrinit.log 2>&1; tail -1 /var/log/frrinit.log'
    kubectl cp ./examples/frr02.cfg frr02:/frr02.cfg
    kubectl exec frr02 -- vtysh -f /frr02.cfg
    

    FRR's daemons keep the output of the command that starts them open, so kubectl exec would not return: the start writes to a file instead.

  • Test (convergence may take time):

    kubectl exec h01 -- ping 172.30.0.2 -I 172.20.0.2
    
cEOS details
  • Start nodes:

    helm install dc3 netclab/netclab --values examples/topology-ceos.yaml --set ceos.image=ceos:<version> --namespace dc3 --create-namespace
    kubectl config set-context --current --namespace dc3
    
    kubectl get pod
    
    NAME     READY   STATUS    RESTARTS   AGE
    ceos01   1/1     Running   0          8s
    ceos02   1/1     Running   0          8s
    h01      1/1     Running   0          8s
    h02      1/1     Running   0          8s
    
  • Configure nodes (repeat if they're not ready yet):

    kubectl exec h01 -- ip address replace 172.20.0.2/24 dev e1
    kubectl exec h01 -- ip route replace 172.30.0.0/24 via 172.20.0.1
    
    kubectl exec h02 -- ip address replace 172.30.0.2/24 dev e1
    kubectl exec h02 -- ip route replace 172.20.0.0/24 via 172.30.0.1
    
    kubectl cp ./examples/ceos01.cfg ceos01:/ceos01.cfg
    kubectl exec ceos01 -- bash -c 'Cli -p 15 /ceos01.cfg'
    
    kubectl cp ./examples/ceos02.cfg ceos02:/ceos02.cfg
    kubectl exec ceos02 -- bash -c 'Cli -p 15 /ceos02.cfg'
    
  • Test (convergence may take time):

    kubectl exec h01 -- ping 172.30.0.2 -I 172.20.0.2
    
  • LLDP neighbor information:

    kubectl exec -ti ceos01 -- Cli -p 15 -c "show lldp neighbors"
    
    Last table change time   : 0:00:48 ago
    Number of table inserts  : 1
    Number of table deletes  : 0
    Number of table drops    : 0
    Number of table age-outs : 0
    
    Port Neighbor Device ID Neighbor Port ID TTL
    ---- ------------------ ---------------- ---
    Et1  ceos02             Ethernet1        120
    
Uninstall topologies
  • dc3:
    kubectl config set-context --current --namespace default
    helm uninstall dc3 --namespace dc3
    kubectl delete ns dc3
    
  • dc2:
    helm uninstall dc2 --namespace dc2
    kubectl delete ns dc2
    
  • dc1:
    helm uninstall dc1
    

🧪 A lab with Crossplane

A lab for Kubernetes-managed network configuration, such as netadopt's AVD fabric, also needs Crossplane and its packages. netclab up installs them after the chart:

uvx netclab up --namespace dc1 --values topology.yaml \
  --crossplane <version> \
  --configuration xpkg.upbound.io/netclab/configuration-avd:<version> \
  --manifest runtime.yaml --manifest providerconfig.yaml --manifest fabric.yaml
  • --configuration installs the package and waits until it is healthy.
  • Each --manifest is applied server-side once the cluster serves every kind in it: before the Configuration if it already does, after it otherwise, in the order given.

🧭 Future Plans

  • Add support for additional containerized or virtualized routers

🤝 Contributing

Found an issue or have an idea? Open an issue or submit a PR at:
👉 https://github.com/netclab/netclab-chart

Metadata

Release files for netclab 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for netclab 0.9.0
File Size Uploaded
netclab-0.9.0.tar.gz 23.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for netclab 0.9.0
File Interpreter ABI Platform
netclab-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 44.3 kB

Release files / netclab-0.9.0.tar.gz

Download URL netclab-0.9.0.tar.gz
Size 23.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2f1ee2d9b18de304b580fc6b12699799eb26994b63a69dbaceeb1854ed065714
BLAKE2b-256 checksum
How to use checksums
ee90c4f18349c2249852a9ed2505fca4bf30dd11cd5a1743d42ad94402d617df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / netclab-0.9.0-py3-none-any.whl

Download URL netclab-0.9.0-py3-none-any.whl
Size 21.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ec41e476661c12a40c0b9310ace3952dc7eea01c57a22e13d13310c5b47a4ad1
BLAKE2b-256 checksum
How to use checksums
b1439f54be43381fb3b809d83c93a1660bb88c4ae30d6ecdf87c98bee12eda7f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page