Skip to content

MikroTik RouterOS CHR#

MikroTik RouterOS Cloud Hosted Router (CHR) from the official mikrotik/chr image is identified with the mikrotik_chr kind in the topology file. The image is published by MikroTik for amd64 and arm64 and is used as is: no vrnetlab build is needed.

The image is a QEMU VM (Alpine, qemu and the CHR disk) in a container. Its own /start.sh is not made for orchestrated links (#3116), so the kind writes a launcher script into the node's lab directory, mounts it read-only and runs it as the entrypoint instead. The launcher uses only the tools that ship in the image.

Note

The mikrotik_ros kind runs CHR from an image you build with vrnetlab. Both kinds use the same interface naming and startup config format, so a lab moves from one to the other by changing kind and image.

Getting MikroTik RouterOS CHR image#

docker pull mikrotik/chr:7.24.5

Tags follow RouterOS versions, plus stable, long-term, testing and development. This kind has been tested with 7.24.5 (stable), 7.23.8 (long-term) and 7.25rc2 (testing).

Managing MikroTik RouterOS CHR nodes#

ssh admin@<node-name>
curl -u admin:admin http://<node-name>/rest/system/resource

Winbox (TCP 8291) and the RouterOS API (TCP 8728, 8729 for TLS) are reachable on the node's management address.

The serial console is on port 5000 inside the container:

docker exec -it <node-name> nc 127.0.0.1 5000

docker exec -it <node-name> sh

The node's management address is RouterOS's own ether1 address in the mgmt VRF (transparent management). Services bound to this VRF answer directly, with nothing forwarded.

Note

Default credentials are admin:admin. With custom credentials the kind creates that user in the full group and disables the factory admin account, which has a blank password on a fresh CHR disk.

Interface naming#

You can use interface names in the topology file like they appear in RouterOS: etherX, where X is the port number.

  • ether2 - first data port
  • ether3 - second data port, and so on

Warning

Data ports start at ether2: ether1 is the management interface. Using ether1 in a topology results in an error.

The container interface ethN is RouterOS ether(N+1), also when links leave gaps: a link on eth12 alone still lands on ether13, with ether2 to ether12 present and unlinked. Interface MTU follows the link MTU, and LLDP frames are forwarded, so /ip neighbor shows the peers.

On AMD64, data NICs are placed behind PCI bridges rather than sharing the default PC's root bus with all other devices. This removes the default layout's limit of 29 total Ethernet interfaces (management plus 28 data ports), so links can use ether30 and higher without a custom launcher. ARM64 still uses a single PCI bus on the virt machine, shared with the disk and the management NIC; the PCI-bridge expansion applies only to AMD64.

Warning

The highest interface depends on the architecture, and larger indices are rejected during deployment:

  • AMD64: eth63 / ether64, the highest port tested. More interfaces exhaust the guest's interrupt vectors (with the default single vCPU) and, from about 200, the virtual PC firmware's PCI address space, which stops the VM before RouterOS boots.
  • ARM64: eth29 / ether30, the last data port that fits on the virt machine's PCI bus.

The check uses the architecture of the containerlab binary, which matches the image's except in emulated setups.

With more than about 30 data ports, RouterOS can reject a NIC while it boots, and the interface does not appear. The launcher checks that RouterOS has an interface for every data NIC and re-plugs the missing ones before the node reports ready; RouterOS keeps their configuration. It checks again after RouterOS reboots itself (for example with /system reboot), when the container keeps running. If a NIC is still missing, the launcher logs data NIC check FAILED and the node is not healthy. The node is also not healthy while RouterOS does not answer within CHR_BOOT_WAIT after such a reboot.

Sparse interface numbering also consumes VM resources for the intervening ports; memory defaults scale with the highest index, not just the number of links.

Node configuration#

MikroTik RouterOS CHR nodes boot with the default CHR configuration plus the credentials, the identity (set to the node name) and the management setup described below.

Startup configuration#

A RouterOS script (.rsc) set with startup-config is imported once the router is up:

name: chr_lab
topology:
  nodes:
    r1:
      kind: mikrotik_chr
      image: mikrotik/chr:7.24.5
      startup-config: r1.rsc

The file is copied to the node's lab directory as ftpboot/config.auto.rsc and imported over the REST API. RouterOS imports line by line and stops at the first error: in that case the container log carries RouterOS's own reason with the line number, for example

chr-launch: startup config import FAILED: Script Error: bad parameter network-type (line 3 column 59); lines before the failing one were applied, the rest were not

and the node does not become ready.

Saving configuration#

containerlab save writes each node's /export to ftpboot/config.auto.rsc in its lab directory. The save filter omits add entries tagged clab-mgmt, plus IPv4/IPv6 address, DHCP-client and IPv6 neighbor-discovery add entries for ether1, even without that tag. These management settings belong to the deployment and are recreated by the launcher; custom entries in those ether1 sections are also omitted. Other interfaces and untagged configuration in other sections are preserved.

Saving explicitly retains an intentional discover-interface-list=static choice, which RouterOS normally omits as its default, so it survives redeployment. The launcher-owned discovery lists are recreated before the saved configuration is imported.

Saving also retains service and SNMP VRF overrides back to main, which RouterOS normally omits as its default.

Repeated exports on RouterOS 7.24.5, 7.25, 7.25rc3 and 7.26beta4 can include an empty, disabled default /system keymat-provider entry that fails to import on a fresh router. Saving omits only that exact entry and leaves out the menu header if the section becomes empty, since RouterOS 7.23.8 does not recognize that menu. Configured default providers and custom providers are preserved. RouterOS 7.23.8 does not emit the entry.

Node readiness#

The node is ready once RouterOS accepted the credentials and the startup config, reaches its gateway from the management address and, unless the configuration disables SSH, its SSH server answers. An SSH server that does not answer within CHR_BOOT_WAIT is logged, and the node is still declared ready, since REST and the console work. The container log then shows

chr-launch: RouterOS ready

and the kind's default healthcheck reports the container healthy, so stages with wait-for can depend on it. A node whose startup config was rejected never becomes healthy. The default healthcheck's start period follows the launcher's own deadlines (CHR_INTF_WAIT + 4 × CHR_BOOT_WAIT + 90 s, 1350 s with the defaults), so a slow boot never reports unhealthy. A healthcheck set in the topology replaces the default one.

Resources#

The kind reads the official image's own environment variables:

Variable Default Purpose
MEM 256 MiB + 8 MiB per data interface and vCPU VM memory (a QEMU -m value, e.g. 1024 or 1G)
CPUS 1 VM vCPUs
DISK 128M minimum disk size (the disk only grows)
CHR_INTF_WAIT 60 seconds to wait for the data interfaces containerlab attaches before the VM starts
CHR_BOOT_WAIT 300 seconds to wait for RouterOS to answer, for the VRF handoff, for the startup config import, and for SSH before the node is declared ready
r1:
  kind: mikrotik_chr
  image: mikrotik/chr:7.24.5
  env:
    MEM: 1024
    CPUS: 2

KVM is used when /dev/kvm is available. Without it the image's QEMU runs under TCG emulation: the node works, but boots several times slower.

Management connectivity#

Management is transparent: the IPv4 and IPv6 addresses and gateways containerlab gives the node's eth0 are configured on RouterOS ether1, and the container's eth0 is bridged to the VM's ether1. The address in the topology table is the one in the RouterOS configuration.

The image ships busybox only (no tc), so where vrnetlab's transparent management redirects with tc-mirred, this kind uses a Linux bridge. The container itself has no address on the management network; it reaches RouterOS for bootstrap over IPv6 link-local on that bridge.

The launcher places ether1 in a native mgmt VRF before main. RouterOS creates its routing table and connected routes; the launcher adds IPv4 and IPv6 defaults that resolve their gateways in mgmt. No source-policy rules or route leaks are added. Data interfaces remain in main, so a lab default route does not take management replies away from ether1. CHR's ether1 DHCP client is disabled.

On first boot, one RouterOS background job creates the VRF and binds SSH, HTTP/HTTPS (including REST), API/API-SSL, Winbox, Telnet and SNMP to it. Their enabled/disabled state is preserved. The launcher waits for the native routing table and service bindings before configuring management addressing, joining the external management bridge and fetching the startup config through mgmt. On restart, startup-config service overrides are preserved. The launcher uses HTTP/REST with the topology credentials during container startup. SSH in mgmt provides user login access and is also used by containerlab save. Credential setup, service ports and access controls are unchanged.

A service's VRF binding also changes where it can be reached: these services no longer listen through data interfaces in main unless the startup config changes their binding. RouterOS FTP does not support VRF binding and is unavailable through the management VRF. Other router-originated services, such as DNS, NTP and logging, need their own VRF-aware configuration to use management egress. RouterOS applies its VRF interface matching to firewall rules; see the RouterOS VRF documentation.

VRF membership does not disable Layer 2 neighbor discovery. The launcher sets an interface list that excludes ether1 by default; see Neighbor discovery for overrides.

RouterOS's router advertisements on ether1 carry a zero router lifetime, so no host on the management network takes the router as its IPv6 default gateway. The container joins the management network only once this is in place. Management objects carry the comment clab-mgmt and are re-applied at boot when the container's addressing changed.

Warning

Do not remove or change the clab-mgmt objects or the mgmt VRF: management of the node depends on them. This setup requires a fresh node container; it does not migrate a running node that uses the previous policy-only mgmt table. Export the user configuration before recreating such a node.

Neighbor discovery#

CHR routers sharing the management bridge can appear in each other's neighbor tables. LLDP, CDP and MNDP are disabled on management ether1 by default; discovery stays enabled on the other static interfaces.

On first boot, before joining the management network, the launcher creates:

/interface list
add name=clab-management comment=clab-mgmt
add name=clab-discovery include=static exclude=clab-management comment=clab-mgmt
/interface list member
add list=clab-management interface=ether1 comment=clab-mgmt
/ip neighbor discovery-settings
set discover-interface-list=clab-discovery

These reserved lists belong to the launcher. To change discovery, select another list in your startup configuration or on the running router. For example, to enable discovery on all static interfaces, including management:

/ip neighbor discovery-settings set discover-interface-list=static

Use discover-interface-list=none to disable discovery everywhere, or select your own interface list. Startup configuration is imported after this default is applied. Restarts preserve the selected policy, including on nodes created before this default was introduced. SSH, REST and other management services remain available. This setting does not control IPv6 address resolution or router advertisements under /ipv6 nd.

As with RouterOS's normal discovery rules, including a bridge or bonding master enables discovery on its slave ports. If your configuration puts ether1 into a bridge or bond, select a discovery list that excludes the master as well.

Restarts#

containerlab restart and a /system reboot inside RouterOS keep the interface names, MAC addresses, configuration and links: the launcher persists the NIC identity in the container on first boot and does not re-run the bootstrap. A plain docker restart keeps the router and its management, but Docker deletes the node's veth links, which stay down until the node is restarted with containerlab or the lab is redeployed.

File mounts#

For each node containerlab creates in its lab directory:

  • ftpboot/ - mounted to /ftpboot, holds config.auto.rsc
  • chr-launch.sh - the launcher, mounted read-only to /chr-launch.sh and rewritten on every deploy