Skip to content

Module Containerlab

sat edited this page Aug 12, 2026 · 15 revisions

Module: Containerlab

This page describes the Containerlab module for dot2net.

What is Containerlab?

Containerlab is a container-based networking lab platform that enables rapid deployment of network topologies using Docker containers. It supports various network operating systems (Nokia SR Linux, Arista cEOS, FRR, etc.) and provides a simple YAML-based topology definition.

What dot2net Generates

The Containerlab module generates topo.yaml, the topology definition file for Containerlab:

name: mylab
topology:
  nodes:
    r1:
      kind: linux
      image: quay.io/frrouting/frr:8.5.4
      network-mode: none
      binds:
      - r1/etc/frr/frr.conf:/etc/frr/frr.conf
      - r1/etc/frr/daemons:/etc/frr/daemons
      exec:
      - vtysh -b
  links:
  - endpoints: ["r1:eth0", "r2:eth0"]

Generated Sections

Section Source Description
name YAML name field Lab name
nodes DOT nodes Node definitions with kind, image, binds, exec
links DOT edges Network connections between nodes

Automatic Features

  • binds: Automatically generated from FileDefinitions that have path set
  • exec: Generated from user-defined startup config block
  • links: Generated from DOT edge definitions

No management network by default

Generated nodes carry network-mode: none, so a lab has only the links its topology describes. containerlab's management network is convenient — clab exec, the clab-* names — but it is also a second path between every pair of nodes, and a reachability test that should have failed can pass through it without anyone noticing. TiNET runs its nodes with --net none and Kathara gives them no management network either, so this also brings the three into line.

module_config:
  containerlab:
    management_network: true   # put it back

Turning it back on means naming the data interfaces something other than eth0, which containerlab keeps for the management interface. dot2net says so while generating rather than letting containerlab refuse at deploy time.

Bridges the topology names

containerlab refuses to deploy while a bridge its topology names does not exist. A scenario that uses a shared medium provided by the platform can pull in a ready-made class, and the module writes the script that makes them:

nodeclass:
  - name: platform_sw
    deploy: platform
    use: [clabOvsBridgeSetup]   # or clabLinuxBridgeSetup
    values:
      kind: ovs-bridge

This writes setup-bridges.sh beside the topology, holding one line per bridge. A lab whose bridges are provisioned some other way — Ansible, sudo rules, a different OVS database — leaves the use: line out and gets no script. The entry script deletes them again when the lab is destroyed.

One topology file per machine

When a scenario declares worker groups, the topology file becomes group-scoped: each machine gets its own, holding its nodes and the links it can wire itself. A link that leaves a machine appears in neither, since no topology file can make it — see Placing nodes on machines.

Bind paths are stated from the machine's directory, because containerlab resolves a relative bind against the directory holding the topology file.

Required Parameters

Each node deployed as a container must have both. A node with deploy: platform — a bridge, a shared medium the platform provides rather than runs — needs only kind: nothing is deployed for it, so there is no image to name.

Parameter Description Example
image Container image quay.io/frrouting/frr:8.5.4
kind Containerlab node kind linux, srl, ceos

Define these in your NodeClass:

nodeclass:
  - name: router
    interface_policy: [p2p]   # addresses for this node's interfaces
    params: [lo]              # and ip_loopback for the node itself
    values:
      image: quay.io/frrouting/frr:8.5.4
      kind: linux

Optional: startup Config Block

The startup config block defines commands to run inside the container. This is used to generate the exec section in topo.yaml.

nodeclass:
  - name: router
    config:
      - name: startup
        template:
          - "vtysh -b"
          - "ip addr add {{ .ip_loopback }}/32 dev lo"

Behavior: a scenario must define a startup template somewhere — containerlab reports node config templates named startup is required without one. A node whose startup comes out empty gets no exec: section, which is where this differs from TiNET: it writes a node_configs entry either way.

Optional: File Bind Mounts

Files with path defined in FileDefinition are automatically mounted into containers:

file:
  - name: frr.conf
    path: /etc/frr/frr.conf    # This triggers bind mount generation
  - name: daemons
    path: /etc/frr/daemons

Generated binds:

binds:
  - r1/etc/frr/frr.conf:/etc/frr/frr.conf
  - r1/etc/frr/daemons:/etc/frr/daemons

Complete Example

input.yaml

name: ospf_lab
module:
  - containerlab
  - frr

file:
  - name: frr.conf
    path: /etc/frr/frr.conf
  - name: daemons
    path: /etc/frr/daemons

layer:
  - name: ip
    default_connect: true
    policy:
      - name: p2p
        range: 10.0.0.0/16
        prefix: 30
      - name: lo
        type: loopback
        range: 10.255.0.0/24

nodeclass:
  - name: router
    interface_policy: [p2p]   # addresses for this node's interfaces
    params: [lo]              # and ip_loopback for the node itself
    values:
      image: quay.io/frrouting/frr:8.5.4
      kind: linux
    config:
      - file: frr.conf
        template:
          - "hostname {{ .name }}"
          - "!"
          - "router ospf"
          - " router-id {{ .ip_loopback }}"
      - file: daemons
        sourcefile: ./daemons
      - name: startup
        template:
          - "vtysh -b"

input.dot

graph  {
  r1 [class="router"]
  r2 [class="router"]
  r1 -- r2
}

Generated topo.yaml

name: ospf_lab
topology:
  nodes:
    r1:
      kind: linux
      image: quay.io/frrouting/frr:8.5.4
      network-mode: none
      binds:
      - r1/etc/frr/frr.conf:/etc/frr/frr.conf
      - r1/etc/frr/daemons:/etc/frr/daemons
      exec:
      - vtysh -b
    r2:
      kind: linux
      image: quay.io/frrouting/frr:8.5.4
      network-mode: none
      binds:
      - r2/etc/frr/frr.conf:/etc/frr/frr.conf
      - r2/etc/frr/daemons:/etc/frr/daemons
      exec:
      - vtysh -b

  links:
  - endpoints: [r1:eth0, r2:eth0]

Running the Lab

module_config:
  containerlab:
    generate_scripts: true

writes containerlab.sh beside the topology, and that is the recommended way to run the lab:

dot2net build -c input.yaml input.dot

sudo ./containerlab.sh deploy
sudo ./containerlab.sh exec r1 vtysh -c "show ip ospf neighbor"
sudo ./containerlab.sh destroy

The script is not a convenience wrapper. destroy runs the lab's teardown commands, copies out the files it collects, destroys the lab and deletes the bridges it made — in that order, reporting any step that failed. None of that happens if containerlab is called directly. See Command Reference.

Without the script, the generated files are still an ordinary containerlab lab:

sudo containerlab deploy -t topo.yaml
sudo containerlab destroy -t topo.yaml --cleanup

See Also

Clone this wiki locally