Containerlab
This page covers the optional containerlab integration: what runs, which PEs are substituted, how to deploy, and known limitations.
Overview
The containerlab artifact (clab-mpls-topology) is generated from Infrahub
data by the clab_topology transform and renders a valid containerlab YAML
topology. It wires up the lab-deployable PE nodes plus the L3VPN CE nodes for
end-to-end testing. The backbone links are data-driven — derived from the
shared /31 addressing rather than fixed in the template — so the lab tracks
whatever backbone the active dataset defines.
Which PEs are deployable depends on the dataset:
- financial (default) — all 8 PEs are Arista cEOS, so all 8 boot in the lab and all 15 backbone links render, plus the 4 cEOS CE routers and their PE-CE access links.
- isp — only the Arista and Nokia PEs have a lab-runnable image, so 2 PE nodes boot with the single backbone link between them.
The topology is written to lab/mpls-backbone.clab.yml by
invoke lab.deploy before containerlab brings it up.
Node map
A device is lab-deployable only when its platform carries a containerlab_os
image. Today that means two vendors:
| Vendor | Platform | Containerlab image | Notes |
|---|---|---|---|
| Arista EOS | arista_eos | ceos-image (see Architecture) | Full configuration push via invoke lab.push-arista |
| Nokia SR OS | nokia_sros | SR Linux (ghcr.io/nokia/srlinux) | SR Linux used in place of SR OS (see below) |
| Cisco IOS-XR | cisco_iosxr | — | No lab image — omitted from the topology |
| Juniper Junos | juniper_junos | — | No lab image — omitted from the topology |
Which PEs that yields depends on the active dataset:
- financial (default) — all 8 PEs (
pe-01…pe-08) are Arista cEOS, so all 8 boot in the lab and all 15 backbone links render. - isp — only
pe-lon-arista(cEOS) andpe-par-nokia(SR Linux) are deployable;pe-fra-ciscoandpe-ams-juniperare omitted.
The lab brings up IS-IS, the full iBGP mesh and PE-CE eBGP, and every customer prefix lands in the right VRF. It does not forward traffic between customer sites: cEOS never starts its LDP agent, so there is no MPLS label path between PEs. See Troubleshooting.
CE nodes are real routers, not stand-ins. On financial the four
pre-provisioned CEs are Arista cEOS and boot from their own ce-arista-eos
configuration artifact, so the PE-CE eBGP session establishes in the lab. A
site whose CE is unmanaged (no ce_device), or whose L3VPN generator has not
run yet, contributes no CE node and no PE-CE link.
Behind each CE the topology also synthesises one customer host — a
nicolaka/netshoot container named cust-<site> (for example cust-ib-zrh
behind ce-ib-zrh). It is lab scaffolding, not Infrahub data: it exists so the LAN
port on the CE has carrier. Without it the port stays down, the dot1q
sub-interface sits lowerlayerdown, and the customer prefix is never advertised
because a down interface contributes no connected route for the BGP network
statement to match. Each host tags its own frames with the same VLAN the CE
expects and takes .10 of the customer subnet, with the CE sub-interface .1
as its default gateway — so the encapsulation is exercised, and the host is a
usable ping source across the VPN.
SR OS → SR Linux substitution
This applies only when the active backbone includes a Nokia PE — i.e. the isp dataset. The default financial backbone is all-Arista, so no substitution happens there.
Nokia's containerlab-friendly image is SR Linux, not SR OS. SR OS is
available only with a paid Nokia licence and is not suitable for a public
demo. The demo uses SR Linux as a stand-in for the Nokia PE. The Infrahub
data model remains nokia_sros for schema consistency; the containerlab
artifact swaps in the SR Linux image automatically.
The SR Linux node boots with the generated Nokia SR OS configuration loaded as a startup configuration, but SR Linux does not parse SR OS CLI syntax. Configuration push to the Nokia PE is therefore not available in v1 (see Known gaps).
Prerequisites
Host
containerlab drives the host kernel directly (network namespaces, veth pairs, bridges), so the lab needs a Linux host. On macOS or Windows, run it inside a Linux VM — OrbStack, Lima, UTM or a cloud instance — not against Docker Desktop from the host side.
The financial dataset is the demanding one: 12 Arista cEOS routers and 4 customer hosts, alongside the Infrahub stack itself.
| Resource | financial (default) | isp |
|---|---|---|
| Lab containers | 16 (12 cEOS + 4 hosts) | 2 (1 cEOS + 1 SR Linux) |
| RAM for the lab | ~24 GB (cEOS wants ~2 GB/node) | ~4 GB |
| RAM including the Infrahub stack | 32 GB or more | ~16 GB |
| vCPU | 8+ recommended | 4 |
A host with less RAM than that does not fail cleanly — cEOS nodes thrash or get
OOM-killed and the deploy stalls in postdeploy. If you have less, use the
isp dataset, or run the Infrahub side only: everything except
invoke lab.deploy works on a modest machine.
Architecture
The lab needs an x86_64 host. Apple Silicon cannot run it.
Every cEOS-lab build Arista publishes has a 32-bit x86 userland — SWI_ARCH=i686
in the image's /etc/swi-version, amd64 tags included. Rosetta 2 does not
translate 32-bit x86 at all, so the only thing left to execute it on Apple
Silicon is qemu-i386 user-mode emulation. Under emulation EOS gets as far as
agentsToStart=[...], loops on ProcMgr.service, and systemd eventually gives
up: the container exits 255 about 17 minutes in, with nothing naming the cause.
There is no arm64 cEOS-lab tag to substitute — a tag whose registry metadata
claims arm64 is a mislabelled x86 image, not an ARM build.
invoke lab.deploy therefore refuses 64-bit ARM hosts outright, in under a
second, rather than letting twelve routers die one at a time:
✗ cEOS-lab cannot run on this host (arm64).
Set LAB_ALLOW_UNSUPPORTED_ARCH=true to attempt it anyway.
Host uname -m | cEOS image |
|---|---|
x86_64 | registry.opsmill.io/external/ceos-image:4.36.0.1F |
aarch64 / arm64 | unsupported — lab.deploy refuses |
The rendered topology reads ${CEOS_IMAGE:=<default>}, so a different build can
be pinned with CEOS_IMAGE without re-rendering the artifact.
On an Apple Silicon machine everything except invoke lab.deploy works
unchanged — schemas, generators, transforms, checks, artifact rendering and the
service catalog. Only the containerlab side needs an x86_64 host.
SR Linux is the exception: ghcr.io/nokia/srlinux:26.7.1-554 is a genuine
multi-arch image and boots natively on ARM. That only covers the single srl
node in the isp dataset; the financial dataset is all cEOS.
Do not pin a cEOS-lab image older than 4.32.0F. Earlier builds require a cgroups v1 host and never finish booting on a cgroups v2 one — the default on Ubuntu 21.04+, OrbStack and most current distributions — and the failure is silent. See Troubleshooting.
Software
- containerlab >= 0.50
- Docker, with network access to pull:
registry.opsmill.io/external/ceos-image(public mirror — no Arista account or manual import needed)ghcr.io/nokia/srlinux(isp dataset only)nicolaka/netshoot(thecust-*customer hosts on the CE LAN side)
- A running Infrahub instance with bootstrap data loaded (
invoke init)
Deploy
# Fetch the clab artifact from Infrahub and write lab/mpls-backbone.clab.yml,
# then call containerlab deploy.
uv run invoke lab.deploy
Expected output (default financial dataset — 8 Arista cEOS PEs, the four pre-provisioned Arista cEOS CE routers, and one customer host behind each CE):
[INFO] Fetching clab-mpls-topology artifact from Infrahub...
[INFO] Writing lab/mpls-backbone.clab.yml
[INFO] Running: containerlab deploy -t lab/mpls-backbone.clab.yml
...
+----+---------------------------------+-----------+---------------------------------+
| # | Name | Kind | Image |
+----+---------------------------------+-----------+---------------------------------+
| 1 | pe-01 | ceos | ceos-image:4.36.0.1F |
| 2 | pe-02 | ceos | ceos-image:4.36.0.1F |
| 3 | pe-03 | ceos | ceos-image:4.36.0.1F |
| 4 | pe-04 | ceos | ceos-image:4.36.0.1F |
| 5 | pe-05 | ceos | ceos-image:4.36.0.1F |
| 6 | pe-06 | ceos | ceos-image:4.36.0.1F |
| 7 | pe-07 | ceos | ceos-image:4.36.0.1F |
| 8 | pe-08 | ceos | ceos-image:4.36.0.1F |
| 9 | ce-ib-lon | ceos | ceos-image:4.36.0.1F |
| 10 | ce-ib-zrh | ceos | ceos-image:4.36.0.1F |
| 11 | ce-trading-lon | ceos | ceos-image:4.36.0.1F |
| 12 | ce-trading-zrh | ceos | ceos-image:4.36.0.1F |
| 13 | cust-ib-lon | linux | nicolaka/netshoot:latest |
| 14 | cust-ib-zrh | linux | nicolaka/netshoot:latest |
| 15 | cust-trading-lon | linux | nicolaka/netshoot:latest |
| 16 | cust-trading-zrh | linux | nicolaka/netshoot:latest |
+----+---------------------------------+-----------+---------------------------------+
On the isp dataset the same command brings up just pe-lon-arista
(Arista cEOS) and pe-par-nokia (Nokia SR Linux), with the single backbone
link between them. That dataset defines no CE routers, and a site without a
ce_device contributes no CE node and no PE-CE link, so the isp lab exercises
the backbone only — use the financial dataset for the PE-CE story.
Push Arista configuration
After the lab is up, push the Arista EOS configuration artifact from Infrahub:
uv run invoke lab.push-arista
This pushes the per-device configs that invoke lab.deploy already wrote to
lab/devices/<node>.cfg, applying each over the containerlab management network
via eAPI. It does not re-fetch them, so if you changed a template since the last
deploy, re-run invoke lab.deploy (or scripts/fetch_lab_configs.py) first —
otherwise the previous render is what reaches the device. On the default
financial backbone every PE is Arista (pe-01…pe-08) plus the four CEs; on
the isp dataset it is the single pe-lon-arista node.
The command shows a diff of what changed. Verify with (financial default —
substitute pe-lon-arista on the isp dataset):
containerlab exec -t lab/mpls-backbone.clab.yml --label clab-node-name=pe-01 \
--cmd "Cli -p 15 -c 'show ip bgp summary vrf all'"
containerlab exec runs its --cmd in the container, not in the EOS CLI, so
the Cli -p 15 -c '…' wrapper is what gets an EOS command executed; without it
the node answers executable file not found in $PATH. show ip bgp summary vrf all covers both the iBGP mesh in the default VRF and each PE-CE session in its
customer VRF. Expect seven Estab neighbours in VRF default on every PE, plus
one per L3VPN site on pe-01 and pe-08.
Note that the VPNv4 address family has no show command on the pinned cEOS
4.29 image — show bgp vpn-ipv4 summary returns % Not supported. That is a
CLI gap, not a signalling failure: the sessions carry the address family, and
show ip route vrf <name> on a hub PE lists the customer prefix learned from
its CE.
Destroy
uv run invoke lab.destroy
This calls containerlab destroy on the generated topology. The topology file
itself is left in place — invoke lab.status and invoke lab.destroy both read
it, so removing it would strand the lab. invoke lab.deploy overwrites it from
Infrahub on the next run.
Lab status
uv run invoke lab.status
Shows container health without modifying anything.
Login credentials
containerlab registers each node in its embedded DNS as
clab-<lab-name>-<node-name>. For the bundled MPLS backbone the lab
name is mpls-backbone-1, so on the default financial dataset the Arista
PEs are at clab-mpls-backbone-1-pe-01 … clab-mpls-backbone-1-pe-08. On the
isp dataset the Arista PE is clab-mpls-backbone-1-pe-lon-arista and the
Nokia PE is clab-mpls-backbone-1-pe-par-nokia.
| Node | Image | User | Password | Notes |
|---|---|---|---|---|
Arista PE (pe-01…pe-08 on financial, pe-lon-arista on isp) | Arista cEOS | admin | admin | containerlab's cEOS default, demo-only |
Arista CE (ce-trading-lon, ce-trading-zrh, ce-ib-lon, ce-ib-zrh) — financial only | Arista cEOS | admin | admin | Same containerlab default, demo-only |
pe-par-nokia (CLI) — isp only | Nokia SR Linux | admin | NokiaSrl1! | SR Linux 22.11+ vendor default |
pe-par-nokia (shell) — isp only | Nokia SR Linux | linuxadmin | NokiaSrl1! | Shell access, same default |
Customer host (cust-trading-lon, cust-trading-zrh, cust-ib-lon, cust-ib-zrh) — financial only | nicolaka/netshoot | root | (none) | Plain Linux container, docker exec only |
To reach the CLIs:
# Arista — via SSH or docker exec (financial default; use pe-lon-arista on isp)
ssh admin@clab-mpls-backbone-1-pe-01
docker exec -it clab-mpls-backbone-1-pe-01 Cli
# Nokia SR Linux (isp dataset only)
ssh admin@clab-mpls-backbone-1-pe-par-nokia
docker exec -it clab-mpls-backbone-1-pe-par-nokia sr_cli
# CE router (financial default) — same cEOS CLI as the PEs
ssh admin@clab-mpls-backbone-1-ce-trading-lon
docker exec -it clab-mpls-backbone-1-ce-trading-lon Cli
# Customer host behind a CE (financial default) — plain shell, no SSH
docker exec -it clab-mpls-backbone-1-cust-trading-lon bash
The cEOS password (admin) is set by the startup-configuration we render —
specifically the username admin privilege 15 role network-admin secret 0 admin
line in templates/pe_arista_eos.j2. It's demo-only; if you ever
publish from this template to real Arista hardware, swap the secret
or gate the line behind a flag.
The Nokia password is the SR Linux 22.11+ default. Older SR Linux
releases used admin as the password — adjust if you've pinned a
different image tag in templates/clab_topology.j2.
Known gaps
| Gap | Details |
|---|---|
| Nokia SR Linux configuration push | SR Linux uses a different CLI/API from SR OS, and the public SR Linux 23.10 image lacks several constructs SR OS supports (LDP, l3vpn-ipv4-unicast afi-safi, bgp-vpn under network-instance). The dedicated pe-nokia-srlinux artifact intentionally renders a lab-minimum configuration — hostname + interfaces + ISIS underlay only. BGP and per-VPN ip-vrf signalling are skipped on the SRL lab node. The production-quality configuration lives in the pe-nokia-sros artifact (real SR OS syntax) and is meant for a real Nokia box. |
| Cisco / Juniper PEs | IOS-XR and Junos container images require vendor licences; these PEs are absent from the v1 lab topology. |
| Inter-site VPN forwarding | PE-CE eBGP establishes and every customer prefix reaches the right VRF, but cEOS never starts its LDP agent, so there is no MPLS label path between PEs and traffic does not cross the backbone. See Troubleshooting. |
| Unmanaged CEs | Only sites with a ce_device get a lab CE. On the isp dataset no site names one, so that lab has no CE nodes, no PE-CE links, and no customer hosts. |
Image pull failures
If containerlab fails with Error response from daemon: pull access denied:
-
SR Linux (
ghcr.io/nokia/srlinux) — requires a GitHub login:docker login ghcr.io -u <github-user> -p <PAT> -
cEOS — the OpsMill mirror (
registry.opsmill.io/external/ceos-image) is public and needs no login, so a pull failure here is usually a tag that does not exist rather than an access problem. Note that the mirror's registry metadata is not a reliable guide to architecture: at least one tag declaresarm64while carrying an x86 image, and pulling it on Apple Silicon succeeds and then fails to boot. Check what you asked for:uname -mecho "${CEOS_IMAGE:-<unset, template default applies>}"To use your own Arista-supplied build instead, import it and point
CEOS_IMAGEat it:docker import cEOS-lab-4.36.0F.tar.xz ceos:localCEOS_IMAGE=ceos:local uv run invoke lab.deploy -
netshoot (
nicolaka/netshoot) — public image used by thecust-*customer hosts; if it fails, check Docker Hub rate limits or mirror it via a local registry.
The CE routers use the same cEOS image as the PEs, so one image covers both.