LUNAROPS · OPERATIONAL UPLINK 100% UPTIME 1,247d POSTS 893 JEFF.MOON@LUNAROPS.DEV UTC --:--:--

Containerlab: Network Labs in Containers

containerlabnetworkingnetwork-automationbgplabs

Network engineers have always had a testing problem. The real protocols — BGP for engineers, OSPF, IS-IS, EVPN/VXLAN — only reveal their behavior under actual control-plane stress. Packet tracers and GUI simulators paper over the details that matter in production. The traditional answer has been GNS3 or EVE-NG: grab some vendor qcow2 images, wrestle them into a topology canvas with a mouse, wait several minutes while full VM instances boot, and then hope the emulated dataplane is close enough to the hardware you’re targeting. That workflow is better than nothing, but it carries a ceiling. Topologies are not version-controlled. Spinning up a fresh lab for each CI run is impractical. Sharing a reproducible environment with a colleague means exporting and transferring multi-gigabyte project archives. Containerlab breaks that ceiling by treating network topology as code — a plain YAML file you can git commit, diff, and deploy in seconds.

Containerlab (project at containerlab.dev, version 0.76.0 as of June 2026) is an open-source CLI tool that orchestrates container-based network labs on any Linux host with Docker. You describe a topology in a .clab.yml file — nodes, their kinds, their images, and the point-to-point links between them — and Containerlab does the rest: pulling images, starting containers, creating Linux veth pairs wired to the right interfaces, building a management network, and dumping an inventory file ready for Ansible. A two-node BGP lab that would take ten minutes to build in GNS3 deploys in under thirty seconds using a Nokia SR Linux image. That gap is not cosmetic; it changes what workflows are even possible.


The Problem With the Old Approach

GNS3 and EVE-NG are not bad tools. They solve a real problem — running vendor network operating systems locally — and they have enormous image ecosystems built up over years. The friction is structural, not accidental.

Both tools were designed around full virtual machine images. Cisco IOSv, Juniper vMX, Arista vEOS: these are complete operating systems packaged as qcow2 or OVA images, each consuming hundreds of megabytes to gigabytes of RAM just to boot. A three-spine, six-leaf EVPN fabric means nine virtual machines, each spending thirty to sixty seconds on Linux kernel bring-up before any routing process starts. You are not paying that cost because the routing protocol requires it; you are paying it because the image is a full OS, not a purpose-built control-plane process.

The GUI compounds the problem. GNS3’s drag-and-drop topology editor is ergonomic for learning, but it is hostile to automation. Topology state lives in a project file that is not human-readable, is not easy to diff, and requires the GNS3 application to interpret. EVE-NG moves the same concept to a browser with shared-server access, which solves the multi-user problem but leaves the automation problem untouched. You cannot git blame a topology you drew with a mouse.

Containerlab’s answer is that network topology is data. A file describes it, deploy instantiates it, destroy tears it down cleanly, and nothing persists between runs unless you explicitly save configuration. The entire workflow fits inside a CI pipeline.


How Containerlab Works

At its core, Containerlab is a thin orchestration layer on top of Docker. When you run containerlab deploy, it reads your .clab.yml, creates a Docker network named clab (default subnet 172.20.20.0/24) that serves as the out-of-band management plane, and starts each node as a container. The data-plane connectivity between nodes is stitched together using Linux veth pairs — virtual Ethernet cables in software — injected directly into each container’s network namespace on the interfaces the topology file specifies. From the perspective of the routing process inside the container, it sees real Ethernet interfaces named whatever the topology file says (e1-1, eth1, Ethernet1, depending on NOS conventions). No overlay protocol, no tunneling, no additional encapsulation — just kernel namespaces and veth pairs behaving like cables.

  topology.clab.yml
        |
        v
  containerlab deploy
        |
  +-----+-------------------------------------+
  |     |                                     |
  |  Docker container: spine1 (SR Linux)      |
  |    eth0 --> clab mgmt network (172.20.20.x)|
  |    e1-1 <---[veth pair]---> e1-1           |
  |  Docker container: leaf1  (SR Linux)      |
  |    eth0 --> clab mgmt network (172.20.20.x)|
  |    e1-1 <---[veth pair]---> e1-1           |
  |  Docker container: leaf2  (SR Linux)      |
  |    eth0 --> clab mgmt network             |
  +-------------------------------------------+

Each node gets a management IP on the clab network, and Containerlab writes a topology-local hosts file mapping node names to those addresses. SSH into spine1 by name. Use containerlab inspect to print the full inventory table with IPs, kinds, and states. The tool also generates a topology-data.json that downstream tools like Ansible or Nornir can consume as a dynamic inventory source.

Node lifecycle is managed with a handful of commands:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# deploy the lab defined in the current directory
containerlab deploy -t fabric.clab.yml

# list all nodes, their management IPs, and container states
containerlab inspect -t fabric.clab.yml

# SSH to a node using its topology name
ssh admin@clab-fabric-spine1

# save running configs back to the lab directory
containerlab save -t fabric.clab.yml

# tear everything down and clean veth pairs
containerlab destroy -t fabric.clab.yml

The save command dumps the running configuration of each node into the lab directory, which lets you capture a working state and commit it alongside the topology file. On the next deploy, you can pass a startup-config path per node to boot each node with a known configuration rather than a blank slate.


The Topology File

The entire lab lives in a single YAML file. Here is a four-node spine-leaf fabric with Nokia SR Linux nodes, sufficient to run BGP unnumbered or EVPN/VXLAN:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
name: spine-leaf

topology:
  defaults:
    kind: nokia_srlinux

  nodes:
    spine1:
      image: ghcr.io/nokia/srlinux:latest
    spine2:
      image: ghcr.io/nokia/srlinux:latest
    leaf1:
      image: ghcr.io/nokia/srlinux:latest
      startup-config: configs/leaf1.cfg
    leaf2:
      image: ghcr.io/nokia/srlinux:latest
      startup-config: configs/leaf2.cfg

  links:
    - endpoints: ["spine1:e1-1", "leaf1:e1-1"]
    - endpoints: ["spine1:e1-2", "leaf2:e1-1"]
    - endpoints: ["spine2:e1-1", "leaf1:e1-2"]
    - endpoints: ["spine2:e1-2", "leaf2:e1-2"]
         spine1          spine2
        /      \        /      \
     e1-1    e1-2    e1-1    e1-2
      |          \  /          |
    leaf1        \/          leaf2
              (cross)
    leaf1:e1-1 -- spine1:e1-1
    leaf1:e1-2 -- spine2:e1-1
    leaf2:e1-1 -- spine1:e1-2
    leaf2:e1-2 -- spine2:e1-2

The topology.nodes block declares each node with a kind (which tells Containerlab how to handle the NOS — what interfaces to expect, how to inject configs, what credentials to use), an image, and optional per-node parameters like startup-config, binds for bind-mounting host files, ports to expose services, and env for environment variables. The topology.links block is a flat list of point-to-point connections, each expressed as a two-element endpoints list in "node:interface" format. That’s the mental model: nodes and cables, expressed as data.

Mixed-vendor topologies are just as straightforward. Replace the kind and image for a node to switch it from SR Linux to FRR or Arista cEOS. A topology file comparing FRRouting in production scenarios against Nokia SR Linux is a few lines of YAML away.


The NOS Ecosystem: Container-Native vs. VM-Wrapped

This is where Containerlab gets honest about the complexity of the network OS world. Not all NOSes are equal, and understanding which tier a given NOS falls into determines resource requirements, boot times, and how closely the emulated behavior matches production.

NOS Kind Image source License required Boot time RAM per node
Nokia SR Linux nokia_srlinux ghcr.io/nokia/srlinux (free, public) None ~10s ~1 GB
FRRouting linux frrouting/frr (Docker Hub, free) None ~3s ~256 MB
SONiC sonic-vs Public images available None ~30s ~1–2 GB
Arista cEOS arista_ceos Arista support portal (free registration) None for lab ~45s ~1–2 GB
Cisco XRd cisco_xrd registry.cisco.com (requires CCO account) Lab license ~60s ~2–4 GB
Cisco IOL cisco_iol Requires VIRL/CML subscription CML license ~30s ~512 MB
Juniper cRPD juniper_crpd Juniper support portal Free for lab ~20s ~512 MB
Juniper vJunos-router (vrnetlab) vr-vjunosrouter Juniper support portal (qcow2) Free for lab ~2–3 min ~4 GB
Cisco Catalyst 8000v (vrnetlab) vr-c8000v Cisco support portal (requires contract) Requires license ~3–5 min ~4 GB

The first three rows — SR Linux, FRR, SONiC — are genuinely free with no account needed, pull from public registries, and run as purpose-built container images. SR Linux in particular is Nokia’s strategic push: they open-sourced the container image precisely to build ecosystem familiarity, and it is a production-quality NOS with a proper YANG model, gNMI, JSON-RPC, and full BGP/OSPF/IS-IS/EVPN support. FRR runs in a plain Linux container with no proprietary components at all — the same Quagga-derived daemon stack that powers many production internet routers.

Arista cEOS is a containerized version of EOS. It requires free registration on the Arista support portal to download, but once you have the image it runs cleanly as a Docker container with realistic EOS behavior. This is the closest you can get to testing Arista-specific features — MLAG, AVT, EOS SDK extensions — without physical hardware.

Cisco XRd is a containerized IOS XR available through registry.cisco.com. You need a Cisco CCO account and a lab license token (available through Cisco’s DevNet program). It runs as a true container but is more memory-hungry than SR Linux or FRR. Cisco IOL (IOS on Linux) is available through the Cisco Modeling Labs subscription, which makes it accessible for engineers already paying for CML.

The vrnetlab Tier

Juniper vMX, vQFX, Cisco Catalyst 8000v, Cisco IOS XRv9000, and similar images require vrnetlab. The srl-labs/vrnetlab project (a fork of the upstream vrnetlab/vrnetlab maintained by the Containerlab team) wraps a full QEMU virtual machine inside a Docker container. The VM boots inside the container, and Containerlab wires the container’s network interfaces into the topology just like any other node. From the topology file’s perspective, a vrnetlab node looks the same as a native container node. The reality inside is a full VM consuming several gigabytes of RAM with a two-to-five minute boot time.

To build a vrnetlab image you download the vendor’s qcow2 or OVA, place it alongside the vrnetlab build scripts, and run make. The resulting Docker image can then be used in any topology. Neither Nokia, Cisco, nor Juniper allows distribution of their VM images, so you build locally. This is not a Containerlab limitation — it is vendor licensing reality. Be aware that nested virtualization must be enabled on the host for vrnetlab nodes to function; cloud VMs may require specific instance types (AWS bare metal, GCP --enable-nested-virtualization).


What You Can Actually Test

The control-plane fidelity of SR Linux, FRR, cEOS, and XRd is production-grade for protocol correctness. These are real routing daemons running real RFC-compliant implementations. What they do not replicate is hardware dataplane behavior: ASIC forwarding rates, hardware-specific TCAM limitations, ECMP hashing behavior tied to merchant silicon, or line-rate performance. For testing whether your BGP policy does what you intend, whether your OSPF for devops engineers area design converges correctly, or whether your EVPN type-5 route leaking configuration works as designed — the control plane is what matters, and that fidelity is real.

Practical workflows that work well on a laptop with 32 GB RAM:

  • BGP unnumbered spine-leaf: four-node topology, interface-based peering, no IP addressing complexity, ECMP validated in minutes
  • EVPN/VXLAN fabric: spines as route reflectors, leaves as VTEPs, symmetric IRB, L3 VXLAN between VRFs
  • Route policy testing: test prefix-lists, route-maps, community tagging and stripping before pushing to production
  • IS-IS multi-area: test summarization and route leaking between levels
  • BGP communities and traffic engineering: verify that communities applied at ingress survive the full AS path and influence egress selection correctly
  • gRPC/gNMI automation: Nokia SR Linux and cEOS both expose full gNMI endpoints; write and test your streaming telemetry collectors or gRPC config push tools against a real NOS API

None of this requires physical hardware. A four-spine, eight-leaf fabric running SR Linux with EVPN deploys in under two minutes and tears down in fifteen seconds.


The Developer and Automation Workflow

The real value proposition of Containerlab over GUI-based tools crystallizes when you think about it as a development environment rather than a study tool. The topology file is a text file. It lives in a git repository alongside the configurations, the Ansible playbooks or Nornir scripts that push to it, and the test assertions that validate behavior.

A canonical CI workflow for network automation looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# .github/workflows/network-ci.yml
name: Network Fabric CI

on:
  pull_request:
    paths:
      - "topology/**"
      - "configs/**"
      - "tests/**"

jobs:
  test-fabric:
    runs-on: self-hosted   # Linux runner with Docker
    steps:
      - uses: actions/checkout@v4

      - name: Install Containerlab
        run: |
          bash -c "$(curl -sL https://get.containerlab.dev)"

      - name: Deploy lab
        run: containerlab deploy -t topology/fabric.clab.yml

      - name: Push configs with Ansible
        run: |
          ansible-playbook -i $(containerlab inspect \
            -t topology/fabric.clab.yml --format json | \
            python3 scripts/gen_inventory.py) \
            playbooks/push_fabric.yml

      - name: Run protocol tests
        run: python3 tests/validate_bgp_sessions.py

      - name: Destroy lab
        if: always()
        run: containerlab destroy -t topology/fabric.clab.yml

This is the workflow that separates “network engineer who uses automation” from “network engineer who practices software engineering”. Your topology is reviewed in a pull request. Config changes deploy to a live virtual fabric before merging. Tests assert that BGP sessions come up, routes are present, prefixes are correctly filtered. If the tests pass, the PR merges and the same configs deploy to staging. The lab spins up on every PR and tears down completely when done, leaving no state behind.

Containerlab generates a clab-<name>/ansible-inventory.yml file after deployment, giving Ansible a ready-made dynamic inventory with hostnames, management IPs, and NOS-specific connection variables. Nornir can consume the same data with a small adapter. The management network is reachable from the host, so any tool that can SSH or speak gNMI to an IP address can interact with the lab nodes.

Startup configurations can be committed to the repository and referenced in the topology file. When the lab deploys, nodes boot with known configs. When you tear down and redeploy, you get a clean, repeatable environment every time. This reproducibility is simply not achievable with a GUI-based tool where topology state is opaque and configurations are embedded in a project archive.


Containerlab vs. GNS3 vs. EVE-NG vs. Plain Docker

Containerlab GNS3 EVE-NG / PNETLab Plain Docker
Topology as code Yes (YAML) Partial (.gns3 JSON) No (GUI-defined) Manual scripting
CI/CD integration First-class Difficult Not practical Possible but complex
Container-native NOSes Yes No No Yes (no wiring)
VM-based images Via vrnetlab Native Native Not built-in
GUI No (CLI only) Yes Yes (browser) No
Multi-vendor support Broad Broad Broad Requires manual work
Boot time (containers) 5–60s N/A (VM-only) N/A (VM-only) 3–30s
Boot time (VMs) 2–5 min (vrnetlab) 1–5 min 1–5 min N/A
Linux host required Yes No (Win/Mac native) Dedicated server Yes
Shared server model Not primary focus GNS3 Server mode Yes (primary model) No
Cost Free/OSS Free/OSS Community/Pro tiers Free

GNS3’s strength is the depth of its image library and the maturity of its GUI, plus native Windows and macOS clients that do not require a Linux host. If you are studying for a Cisco certification and want to run IOS images with a visual topology canvas, GNS3 is still a reasonable choice. Its weaknesses are automation hostility and the VM-only architecture that makes large topologies expensive.

EVE-NG (and its community fork PNETLab) solves the multi-user, shared-lab problem well. The browser-based interface means any engineer on the network can access the same running topology. EVE-NG Pro 6.4 (January 2026) added MFA, encrypted lab state, and EVE Cluster for multi-host distribution — features that matter for enterprise training environments and large teams. What it does not solve is the automation workflow: topologies are drawn, not declared, and the system is not designed for ephemeral CI-driven lab runs.

Plain Docker can wire containers together using Docker networks and manually created veth pairs, but it provides no NOS-specific lifecycle management, no startup-config injection, no inventory generation, and no abstraction over the topology. You would be reinventing pieces of Containerlab with shell scripts. It is instructive as an exercise and impractical as a workflow.


Honest Limits

Containerlab’s architecture brings genuine tradeoffs that are worth stating plainly rather than burying in footnotes.

Vendor images and licensing are still your problem. SR Linux, FRR, and SONiC are freely available with no account or license. Everything else — Arista cEOS, Cisco XRd, Cisco IOL, Juniper cRPD — requires you to navigate vendor portals, create accounts, agree to license terms, and sometimes pay for access. The vrnetlab-wrapped full VM images (Cisco Catalyst 8000v, IOS XRv9000, Juniper vMX) are often tied to support contracts or require active CML subscriptions. Containerlab provides the machinery; the images remain vendor-controlled. Do not expect to run a Cisco-heavy topology without a Cisco relationship.

Dataplane fidelity is control-plane only. Running BGP for engineers scenarios or OSPF simulations will give you accurate protocol behavior. Testing whether a specific ASIC performs symmetric hashing under IPv6 ECMP, or whether a Nexus 9000’s hardware FIB handles a specific scale, is not something a software-emulated container can answer. If you are validating hardware-dependent behavior, the lab supplements but does not replace physical gear or high-fidelity simulators like Cisco Modeling Labs.

vrnetlab nodes are heavy. A topology with four vrnetlab-wrapped VMs will consume 16–20 GB of RAM and require a host with nested virtualization. On a laptop this may mean running Containerlab in a dedicated VM or a beefy cloud instance. The lightweight SR Linux/FRR/cEOS tier is where the “runs on a laptop” story actually holds.

It is a CLI tool. There is no visual canvas. Writing a twelve-node topology YAML file is not difficult, but it requires comfort with structured text files and some patience. Engineers coming from a pure GUI background will face a learning curve. The payoff is automation and reproducibility; the cost is the initial friction of learning to describe topology declaratively rather than drawing it.

Linux host is required. Containerlab runs on Linux with Docker. The wsl-containerlab project provides a ready-to-use WSL2 distribution for Windows users, and macOS support via Lima/Colima or similar Linux VM wrappers exists but is not first-class. If your primary machine is macOS or Windows, plan for an extra layer.


Verdict

Containerlab has earned its position as the tool of choice for engineers who treat network configuration as software. The declarative topology file, the container-native NOS ecosystem anchored by Nokia SR Linux and FRR, the CI integration story, and the sub-minute deploy/destroy cycle represent a genuine paradigm shift from the GNS3 era. For an engineer building EVPN fabrics, writing automation that exercises BGP policy, or validating OSPF for devops engineers designs before they reach hardware, Containerlab offers a workflow that would have been impractical five years ago.

It is not a complete replacement for GUI-based tools for every audience. Students learning network fundamentals may find the visual topology canvas of GNS3 more accessible. Teams needing shared persistent labs with browser access will find EVE-NG’s architecture better suited. And no container-based tool removes the need for real hardware when ASIC-specific behavior matters.

But if you are writing network automation code, testing configuration changes in a pull request workflow, or building any kind of programmatic interaction with network infrastructure, the question is not whether Containerlab fits — it is why you would use anything else. Start with a two-node SR Linux topology, wire it with FRR, and run BGP between them. It deploys in thirty seconds. The rest follows naturally.


Sources

Comments