Designing a Leaf-Spine Fabric Without the Hand-Waving

Every network engineer has owned a lab that rotted. You build a topology in a GUI emulator, wire it by hand, configure six routers, and it works — until a month later when you reopen it and something is subtly different, or the VM is gone, or you cannot remember why interface three had that one static route. The lab was never reproducible, so it was never really trustworthy. Containerlab fixes that by making the topology a text file you commit to Git, spin up in seconds, and tear down without leaving anything behind. This is the full setup, start to finish.

The reproducibility problem

Traditional emulators — the GNS3 and EVE-NG lineage — treat a lab as stateful infrastructure. You draw it, you boot full VMs, you configure them interactively, and the state lives inside the VM’s disk. That is heavy (a handful of router VMs will eat your RAM), slow to start, and fundamentally unversioned. The thing that defines your lab is a binary blob and your memory of what you clicked. Share it with a colleague and you are exporting gigabytes and hoping it imports cleanly.

Containerlab takes the opposite stance. The lab is declared in a YAML file: which nodes exist, what image each runs, and how they are wired. The containers are lightweight — network OSes packaged as containers boot in seconds, not minutes — and the definition is a few kilobytes of text. The lab is the file. Lose the running containers and you lose nothing, because one command rebuilds them identically.

Why FRR

FRR (Free Range Routing) is an open-source routing stack — the descendant of Quagga — that speaks BGP, OSPF, IS-IS, and more with a Cisco-like CLI (vtysh). It ships as a container, it is free, and it runs anywhere. For learning the protocols, building a fabric you want to test before touching production, or reproducing a routing bug, FRR is ideal: the config concepts map directly onto commercial kit, so a BGP design you prove in FRR translates cleanly to Arista, Cisco, or Juniper. And because it is just a container, you can run a dozen instances on a laptop.

Containerlab is not limited to FRR, which is the other half of its appeal. The same topology file can mix FRR with vendor network OSes that ship as containers — Nokia SR Linux, Arista cEOS, Juniper’s container offerings — so you can build a genuinely multi-vendor topology and test interop, all from one declarative file. Start with FRR because it is free and frictionless; reach for the vendor images when you need to validate something vendor-specific.

Installing

Containerlab runs on Linux (or WSL2/Linux VM on other hosts) and needs a container runtime — Docker is fine. Installation is a single script:

# install containerlab
bash -c "$(curl -sL https://get.containerlab.dev)"
 
# verify
containerlab version

Pull the FRR image once and you are ready. Everything after this is just topology files.

Defining a topology

Here is a complete, useful lab — a small leaf-spine fabric, two spines and two leaves, each running FRR. The whole thing is one file:

# fabric.clab.yml
name: fabric
 
topology:
  kinds:
    linux:
      image: quay.io/frrouting/frr:9.1.0
  nodes:
    spine01: { kind: linux, startup-config: cfg/spine01.cfg }
    spine02: { kind: linux, startup-config: cfg/spine02.cfg }
    leaf01:  { kind: linux, startup-config: cfg/leaf01.cfg }
    leaf02:  { kind: linux, startup-config: cfg/leaf02.cfg }
  links:
    - endpoints: ["leaf01:eth1", "spine01:eth1"]
    - endpoints: ["leaf01:eth2", "spine02:eth1"]
    - endpoints: ["leaf02:eth1", "spine01:eth2"]
    - endpoints: ["leaf02:eth2", "spine02:eth2"]

Two things make this reproducible. The links list is the wiring — explicit, readable, and diffable, so a change to the topology shows up cleanly in a pull request. And each node points at a startup-config file, so the routers come up already configured. There is no manual setup step to forget; the config is part of the committed lab.

Bring it up, tear it down

Two commands run the whole lifecycle:

# deploy the lab
containerlab deploy -t fabric.clab.yml
 
# drop into a router
docker exec -it clab-fabric-leaf01 vtysh
 
# destroy it, leaving nothing behind
containerlab destroy -t fabric.clab.yml --cleanup

Deploy builds the containers, wires them per the file, and pushes each startup-config — a few seconds, not the several minutes a VM lab takes. destroy --cleanup removes the containers and the working directory entirely. Nothing lingers on the host, no half-configured VM waiting to confuse you next month. Need it back? Deploy again and you get a byte-for-byte identical fabric.

Version it in Git

This is where it stops being a lab and becomes an asset. The topology file and the per-node startup-configs go in a Git repo:

fabric/
  fabric.clab.yml
  cfg/
    spine01.cfg
    spine02.cfg
    leaf01.cfg
    leaf02.cfg

Now the lab is reviewable, shareable, and historical. A colleague clones the repo and runs one command to get exactly your fabric. You branch to try a different BGP design without disturbing the working one. You can point CI at it — deploy the lab, run a script that checks every BGP session came up, tear it down — so a change to the configs is automatically tested. The lab that used to rot in a GUI is now the same kind of artefact as your code.

Where to take it next

Once the workflow clicks, the lab becomes the obvious place to prove things before they reach production. Test a routing-policy change against a copy of your real topology. Reproduce a customer’s bug by rebuilding their setup from a file. Add a Nokia or Arista node to validate a multi-vendor interop question. Generate the startup-configs from the same Jinja2 templates that build your production fleet, so you are testing the actual automation, not a hand-written approximation of it.

The shift is small but it changes everything: a lab stops being a fragile thing you are afraid to lose and becomes something you can destroy a hundred times a day because rebuilding it is free. That is what reproducible means — not that the lab survives, but that it never needed to.

Share this:

Leave a Reply

Your email address will not be published. Required fields are marked *