Over the past year we have brought 11 different network operating systems onto containerlab — Nokia SR Linux, FRR, Cisco IOL, IOS XRd and NX-OS, Juniper cRPD, Arista cEOS, Palo Alto PAN-OS, Fortinet FortiOS, Aruba AOS-CX, and Dell SmartFabric OS10 — as the sandbox layer behind NetPilot, where an AI agent builds and validates labs on top of them. Most of that work was not topology design. It was the unglamorous last 10%: image packaging, boot readiness, and per-NOS quirks.
This post collects the lessons that would have saved us weeks. If you run multi-vendor containerlab topologies — by hand or through automation — these are for you.
Bottom line: containerlab itself is remarkably solid. Nearly every hard problem we hit lived in the seams around it: image naming conventions, control-plane readiness, interface aliasing, and version drift.
1. Image packaging: the filename is part of the contract
Containerized NOSes (SR Linux, cEOS, cRPD, FRR) are straightforward: import the image, reference it in your topology YAML, done. VM-based NOSes — and a few native binaries like Cisco IOL — are packaged through vrnetlab, which wraps each image in a container. vrnetlab's build scripts encode assumptions about the image filename that are easy to trip over.
Example: building Cisco IOL requires the binary to match the cisco_iol-<version>.bin pattern
the build tooling expects. Rename the file you downloaded from Cisco to match the convention
before building, or the build fails in ways that don't obviously point at the filename:
# vrnetlab expects the naming convention, not Cisco's download name
mv x86_64_crb_linux-adventerprisek9-ms.bin cisco_iol-17.12.01.bin
make docker-imageThe general rule: before debugging anything else in a vrnetlab build, read the Makefile of that
platform's directory and check what filename glob it matches. Two of our longest debugging
sessions ended exactly there.
One more thing that belongs in this section: image licensing. Containerized or not, every commercial NOS image is licensed by its vendor. Bring your own images obtained through your vendor accounts — nobody in this ecosystem (including us) distributes them for you.
2. "Running" is not "ready"
Docker considers a node up when PID 1 starts. A network OS considers itself up minutes later — after the OS boots, the startup config applies, protocol daemons start, and SSH begins accepting sessions. The gap between the two is where flaky lab automation is born.
What we converged on, per NOS:
- Wait for a real login, not a TCP port. An open port 22 does not mean the CLI will accept a session; some NOSes accept the TCP handshake while the daemon is still initializing.
- Wait for a usable prompt. After login, confirm you can reach a config-capable prompt. On slow-booting VM platforms, the CLI can be up while the management plane still refuses configuration commands.
- Budget wildly different boot times. FRR and cRPD are ready in seconds; SR Linux and cEOS in tens of seconds; VM-based platforms like NX-OS — and heavyweight container-native ones like XRd — can take several minutes on modest hardware. A single global timeout either wastes minutes on the fast platforms or gives up too early on slow ones — use per-kind budgets.
If you script this yourself, a per-node Docker healthcheck in your topology definition gets
you part of the way; a small per-NOS readiness probe (login, expect a usable prompt, run one
harmless show command) gets you the rest. It is the single highest-leverage reliability investment we made.
3. Interface naming is where multi-vendor labs go to die
Every NOS names interfaces differently — ethernet-1/1 (SR Linux), Ethernet1 (cEOS),
plain Linux eth1 (cRPD), Ethernet0/1 (IOL), ethernet1/1/1 (Dell OS10) — and containerlab bridges
that gap by mapping topology-level link definitions to per-kind interface aliases. That mapping
is regex-driven per platform, and when a platform's convention isn't covered, links silently
fail to wire the way you expect.
We hit exactly this bringing up Dell SmartFabric OS10: its ethernet1/1/x alias format wasn't
matched by the interface-alias regexp, so aliased links didn't map (issue #3352).
The fix was small and we sent it upstream (PR #3354).
Two takeaways:
- When adding a new NOS kind, verify link wiring first — before debugging any protocol.
show lldp neighbors(or the platform's equivalent) on day one saves you from chasing phantom OSPF problems that are actually cabling problems. - Upstream your fixes. The containerlab maintainers are responsive, and every platform quirk you fix upstream is one you never patch again after an upgrade.
4. Interactive dialogs and pagination will eat your automation
Several platforms greet a fresh boot with interactive dialogs — initial-configuration wizards, password-change prompts, license acknowledgments — and nearly all of them paginate long command output by default. Human operators click through these without noticing; automation hangs on them forever.
Our rules of thumb:
- Handle first-boot dialogs explicitly per platform (answer or disable the wizard in startup config where the platform supports it).
- Disable pagination as the first post-login command, every session, every platform —
terminal length 0,set cli screen-length 0,no page, or the local equivalent. - Treat prompt detection as a per-platform contract. Enable/config/transaction prompts
differ enough across NOSes that generic "wait for
#" logic eventually misfires — usually at the worst possible time, inside a config session.
5. Pin your containerlab version — and test upgrades like releases
Containerlab develops fast, which is mostly great. But generated artifacts and behaviors can change between minor versions in ways your surrounding tooling may silently depend on — we've been bitten by changes in generated files during an upgrade that our tooling assumed were stable. Since then:
- Pin the version everywhere labs run.
- Read the release notes for every minor bump.
- Test the upgrade against the full topology matrix — every NOS kind you run, not a sample — before rolling it out. Platform-specific regressions concentrate exactly in the kinds you don't test.
6. Resource budgeting per NOS kind
A quick reference from our fleet experience, for sizing mixed topologies (rough, per node, and generous — tune down after measuring your own):
| NOS class | Examples | Typical footprint |
|---|---|---|
| Lightweight container-native | FRR, cRPD, Cisco IOL | Very small — tens to hundreds of MB RAM |
| Container-native switch/router | SR Linux, cEOS | Moderate — roughly 1–2 GB RAM |
| Heavy container-native | Cisco XRd | Large — several GB RAM plus reserved vCPUs |
| VM-in-container (vrnetlab) | NX-OS, PAN-OS, FortiOS, OS10, AOS-CX | Large — 2–8+ GB RAM, dedicated vCPUs |
The practical consequence: mixed-vendor topologies are dominated by their heaviest kinds. Eight FRR nodes cost less than one NX-OS node, so put the vendor-specific platforms only where the test actually needs vendor-specific behavior and use lightweight kinds for the rest of the fabric.
What this adds up to
None of these lessons is hard on its own. Together they are why "just spin up a multi-vendor lab" routinely turns into a lost week for teams doing it the first time — the topology takes an hour, and the seams take the rest.
That gap is the reason NetPilot exists: the agent carries these per-NOS contracts — image conventions, readiness probes, dialog handling, interface mappings — so you describe the lab you want in plain English and get a running multi-vendor topology with real CLIs you can SSH into and verify by hand. If you'd rather build the automation yourself, we hope the lessons above save you some of the weeks they cost us.
Copy-paste ready: multi-vendor OSPF area 0 lab prompt from our example-prompts repo.
Related reading: What Is ContainerLab? The Complete 2026 Guide · Best Network Emulator 2026 · Open Networking Research Lab
FAQ
Can containerlab run Cisco, Juniper, and Arista images?
Yes. Natively containerized NOSes like Nokia SR Linux, Arista cEOS, Juniper cRPD, Cisco IOS XRd, and FRR run directly as containers. Cisco IOL is a lightweight native binary packaged with vrnetlab tooling, and VM-based images such as NX-OS and most firewall platforms run inside containers via the vrnetlab project. You must supply your own licensed images for commercial NOSes.
Why does a containerlab node show running but not accept SSH?
Container state and control-plane readiness are different things. Docker reports the container as running as soon as PID 1 starts, but a network OS may need minutes more to finish booting, apply startup config, and start its SSH daemon. Reliable automation waits on a per-NOS readiness signal, such as a successful login and a config-mode prompt, not on container state.
Should I pin my containerlab version in production labs?
Yes. Containerlab moves fast, and minor releases can change generated artifacts and behaviors that your tooling may depend on. Pin the version, read release notes, and test upgrades against your full topology matrix before rolling them out.
Ready to skip the seams? Try NetPilot free — describe a multi-vendor lab in plain English and the agent builds it, carrying every per-NOS contract in this post for you; lightweight topologies are ready in about 2 minutes, and heavyweight NOSes take only as long as their own boot times.