The fastest way to run MikroTik RouterOS CHR in containerlab is the mikrotik_chr kind with the official mikrotik/chr image from Docker Hub. There is no disk image to download and no vrnetlab build. You pull the image, write a short topology file, and deploy.
The NetPilot team contributed this kind to containerlab, and it shipped in v0.82.0. Its reference page is in the containerlab documentation, and the merged upstream work is listed on our site. This post walks through a two-router OSPF lab. Every command below ran on containerlab v0.82.0 with RouterOS 7.24.5, and every output is copied from that run.
Ways to run MikroTik CHR
| Route | Image step | Multi-node links | Runs on | Best for |
|---|---|---|---|---|
| VirtualBox or VMware | Download the CHR disk image | ⚠️ Manual per VM | Your PC | One router, first contact |
| GNS3 or EVE-NG | Import the CHR image | ✅ Drag and drop | Your PC or lab server | Graphical labs, offline study |
containerlab mikrotik_ros | Build with vrnetlab | ✅ Topology file | Linux host | Existing vrnetlab pipelines |
containerlab mikrotik_chr | docker pull | ✅ Topology file | Linux host | Labs as code, CI |
| NetPilot | Preloaded on every lab VM | ✅ AI agent from a prompt | Browser | No host to run, mixed-vendor labs |
Bottom line: if you have a Linux host,
mikrotik_chris the shortest path to a CHR lab you can keep in Git. If you would rather not run a host, NetPilot preloads the samemikrotik/chrimage on every lab VM, and an AI agent builds the lab from a prompt in about 2 minutes.
What you need
- containerlab v0.82.0 or newer on a Linux host with Docker
- About 300 MB of memory per CHR node
- KVM if you have it. Without
/dev/kvmthe node runs under QEMU emulation and boots several times slower
Step 1: Pull the image
docker pull mikrotik/chr:7.24.5Tags follow RouterOS versions, plus stable, long-term, testing and development. MikroTik publishes the image for amd64 and arm64.
Step 2: Get the lab
The files are in our public mikrotik-labs repository:
git clone https://github.com/netpilot-labs/mikrotik-labs
cd mikrotik-labs/01-chr-two-routersThe topology is short:
name: chr-two-routers
topology:
kinds:
mikrotik_chr:
image: mikrotik/chr:7.24.5
nodes:
r1:
kind: mikrotik_chr
startup-config: r1.rsc
r2:
kind: mikrotik_chr
startup-config: r2.rsc
links:
- endpoints: ["r1:ether2", "r2:ether2"]Data ports start at ether2. ether1 is the management interface, and using it in a link is an error.
The startup config is a plain RouterOS script. This is r1.rsc:
/interface bridge add name=loopback0
/ip address add address=10.0.12.1/30 interface=ether2
/ip address add address=10.255.0.1/32 interface=loopback0
/routing ospf instance add name=default router-id=10.255.0.1 version=2
/routing ospf area add name=backbone area-id=0.0.0.0 instance=default
/routing ospf interface-template add area=backbone interfaces=ether2 type=ptp
/routing ospf interface-template add area=backbone interfaces=loopback0 passiveRouterOS has no loopback interface menu, so an empty bridge holds the /32. RouterOS imports the script line by line and stops at the first error. The container log then shows the line and column, and the node does not become ready.
Step 3: Deploy
sudo containerlab deploy -t chr-two-routers.clab.ymlDeploy returns right away, while RouterOS is still booting. Check the health column:
sudo containerlab inspect -t chr-two-routers.clab.yml╭─────────────────────────┬─────────────────────┬───────────┬───────────────────╮
│ Name │ Kind/Image │ State │ IPv4/6 Address │
├─────────────────────────┼─────────────────────┼───────────┼───────────────────┤
│ clab-chr-two-routers-r1 │ mikrotik_chr │ running │ 172.20.20.2 │
│ │ mikrotik/chr:7.24.5 │ (healthy) │ 3fff:172:20:20::2 │
├─────────────────────────┼─────────────────────┼───────────┼───────────────────┤
│ clab-chr-two-routers-r2 │ mikrotik_chr │ running │ 172.20.20.3 │
│ │ mikrotik/chr:7.24.5 │ (healthy) │ 3fff:172:20:20::3 │
╰─────────────────────────┴─────────────────────┴───────────┴───────────────────╯On our host with KVM, each node took about 50 seconds from QEMU start to healthy. The log line that marks the end is chr-launch: RouterOS ready, which you can read with docker logs clab-chr-two-routers-r1.
Step 4: Verify on the RouterOS CLI
Log in with admin and the password admin:
ssh admin@clab-chr-two-routers-r1LLDP frames cross the link, so the neighbor table already shows the other router:
[admin@r1] > /ip neighbor print
Columns: INTERFACE, ADDRESS4, ADDRESS6, MAC-ADDRESS, IDENTITY
# INTERFACE ADDRESS4 ADDRESS6 MAC-ADDRESS IDENTITY
0 ether2 10.0.12.2 fe80::5054:ff:fe20:4d01 52:54:00:20:4D:01 r2OSPF reaches Full:
[admin@r1] > /routing ospf neighbor print
Flags: D - DYNAMIC
0 D instance=default area=backbone interface=ether2 address=10.0.12.2
router-id=10.255.0.2 state="Full" state-changes=6 adjacency=5s timeout=36sThe routing table shows the far loopback, plus a separate mgmt table:
[admin@r1] > /ip route print
Flags: D - DYNAMIC; A - ACTIVE; c - CONNECT, s - STATIC, o - OSPF
Columns: DST-ADDRESS, GATEWAY, ROUTING-TABLE, DISTANCE
# DST-ADDRESS GATEWAY ROUTING-TABLE DISTANCE
DAc 10.0.12.0/30 ether2 main 0
DAc 10.255.0.1/32 loopback0 main 0
DAo 10.255.0.2/32 10.0.12.2%ether2 main 110
DAc 172.20.20.0/24 ether1 main 0
;;; clab-mgmt
0 As 0.0.0.0/0 172.20.20.1 mgmt 1
;;; clab-mgmt
1 As 172.20.20.0/24 ether1 mgmt 1That mgmt table is why the lab stays reachable. Management return traffic has its own table, so a default route you add to main, for a dual-WAN test for example, does not cut SSH to the node. Leave the clab-mgmt objects alone, because management depends on them.
A ping between loopbacks proves the route works:
[admin@r1] > /ping 10.255.0.2 count=3 src-address=10.255.0.1
SEQ HOST SIZE TTL TIME STATUS
0 10.255.0.2 56 64 321us
1 10.255.0.2 56 64 346us
2 10.255.0.2 56 64 356us
sent=3 received=3 packet-loss=0% min-rtt=321us avg-rtt=341us
max-rtt=356usOther ways in
The node's management address is RouterOS's own ether1 address, so every service answers there with nothing forwarded. The REST API works right away:
curl -u admin:admin http://clab-chr-two-routers-r1/rest/system/resourceWinbox answers on TCP 8291 of the same address. The node's name resolves on the lab host, and the address in the inspect table works from anywhere that can route to it.
Save your work
containerlab save writes each node's /export to ftpboot/config.auto.rsc in its lab directory, without the management objects, so the file works as the startup config of the next deploy. destroy --cleanup deletes the lab directory, so copy the files out first:
sudo containerlab save -t chr-two-routers.clab.yml
sudo cp clab-chr-two-routers/r1/ftpboot/config.auto.rsc r1.rsc
sudo cp clab-chr-two-routers/r2/ftpboot/config.auto.rsc r2.rsc
sudo containerlab destroy -t chr-two-routers.clab.yml --cleanupThe free CHR license
The image runs with the CHR free license, which you can see on the node:
[admin@r1] > /system license print
system-id: WVunSnChl0J
level: freeMikroTik documents the free level as running indefinitely, limited to 1 Mbps upload per interface, with all other features available. In our MTCNA lab iperf3 read about 1.3 Mbit/s through a CHR router. Routing, firewall and control plane labs are unaffected. Throughput tests are the one job this license does not fit.
The same lab from a prompt
If you do not want to run a Linux host, describe the lab instead:
"Build two MikroTik CHR routers connected on ether2, with OSPF in area 0 and a loopback on each. Verify the adjacency and ping loopback to loopback."
On NetPilot the agent designs the topology, writes the RouterOS configs, deploys on containerlab in the cloud, and runs the checks. Direct CLI is always available too: open a terminal on any router and run /routing ospf neighbor print yourself. The mikrotik/chr image is preloaded on every lab VM, next to the other network OSes on the supported devices list.
FAQ
How do I run MikroTik CHR in containerlab?
Install containerlab v0.82.0 or newer, set kind: mikrotik_chr and image: mikrotik/chr:7.24.5 on a node in the topology file, and run containerlab deploy. The image is used as published, so no vrnetlab build is needed. Data links start at ether2 because ether1 is the management port.
What is the difference between mikrotik_chr and mikrotik_ros in containerlab?
The mikrotik_chr kind runs the official mikrotik/chr image as MikroTik publishes it. The mikrotik_ros kind runs CHR from an image you build yourself with vrnetlab. Both use the same interface names and startup config format, so a lab moves from one to the other by changing kind and image.
What are the default credentials for a CHR node in containerlab?
The default login is admin with the password admin. If you set custom credentials on the node, the kind creates that user in the full group and disables the factory admin account.
Is the free CHR license enough for a lab?
Yes for routing, firewall and control plane work. MikroTik documents the free license as limited to 1 Mbps upload per interface and says all other CHR features are available without restrictions. In our lab iperf3 read about 1.3 Mbit/s through a CHR router on the free license.
Does the mikrotik_chr kind need KVM?
No. The kind uses KVM when /dev/kvm is available. Without it QEMU runs under TCG emulation, and the node works but boots several times slower.
Next labs
- MTCNA practice lab: DHCP, bridging, NAT, firewall, queues and PPPoE on CHR
- MikroTik load balancing with PCC: dual WAN with failover on RouterOS 7
- MikroTik lab online: the hub for all of them
NetPilot is a containerlab contributor and integrator. MikroTik and RouterOS are trademarks of their owner, and NetPilot is not affiliated with MikroTik.