Skip to content

The router’s CPU, the container’s network

A RouterOS container shares the router’s kernel, but not all of the kernel’s view. This page answers which files inside the container describe the router and which describe only the container itself, what that means for per-interface traffic and connection counts, and why no container setting mikroscope could choose moves the line.

The CPU, interrupt, memory and block-device files are global: inside the container they are the router’s own, and they are read on every tick (a block device’s row is stored only when it did something). Established on the RB5009 (RouterOS 7.24.2, kernel 5.6.3, 2026-09-11):

  • /proc/stat, per core
  • /proc/interrupts and /proc/softirqs
  • /proc/net/softnet_stat: drops and time squeezes in the kernel’s receive path. It lives under /proc/net but counts per CPU, not per namespace.
  • /proc/meminfo, /proc/vmstat and /proc/loadavg
  • /proc/diskstats

An ordinary container also reads these files as the router’s, read on 2026-09-12: the two thermal zones under /sys/class/thermal, scaling_cur_freq per core, the NAND wear counters in /proc/yaffs and /proc/buddyinfo. The device tree’s model string (RB5009) is not namespaced either, and it is how the agent identifies the board without the RouterOS API.

/proc/net/dev, /proc/net/snmp, /proc/net/netstat and nf_conntrack_count are per network namespace. Inside the container they describe the container’s own veth: 4 packets while the router forwarded millions. The agent does not read them as router data, and /capabilities lists them under namespaced so a consumer can see they were left out on purpose rather than missed.

privileged=yes does not change this. It drops the container’s user namespace, not its network namespace:

Measured on RB5009UG+S+ · 4 × 1.4 GHz Cortex-A72 · RouterOS 7.24.2 · · privileged=yes does not change the network namespace

So per-interface bytes and packets come from the RouterOS API instead, and the collector merges them with the kernel tier on the agent’s clock. They are not interpolated from anything the container can see. How that tier is polled, and how little of it is left, is on the RouterOS API tier.

The same boundary shows up in other places, each measured on the RB5009:

What What the container sees Measured
/sys/class/net only lo and the veth; no device for any front-panel port, privileged or not 2026-09-14
/sys/class/mdio_bus, /sys/class/phy mdio_bus holds only fixed-0; phy is empty 2026-09-14
netdev_budget, netdev_max_backlog and the other global net.core entries absent; per-namespace entries such as somaxconn are present 2026-09-14
/proc/net/* files created by modules the container’s own: fib_trie shows only the veth’s /30, snmp6 counts its packets 2026-09-15
nf_conntrack_max the router’s real ceiling, 966 656, the same as RouterOS reports as max-entries 2026-09-14
conntrack timeouts Linux defaults (tcp_timeout_established 432 000 s against RouterOS’s 1d) 2026-09-14

The last two rows sit in the same directory and split in opposite directions. nf_conntrack_max is read once at start and shipped as the connection table’s ceiling. The timeouts are never presented as the router’s configuration, because they are not.

The absent netdev_budget is also why the squeeze panels have no budget denominator: that is the namespace’s doing, not the agent’s.

The connection count is the exception. Under privileged=yes the agent reads /proc/slabinfo, and the slab allocator is global: the nf_conntrack cache’s active-object count is the router’s real conntrack population. On the RB5009 on 2026-09-12 it read 6 582 in the discovery container and 6 287 from the agent the same day, while the container’s own namespace reported 0; the RouterOS API had counted 6 212 the day before.

That replaces an API table scan with a file read, and it is why the collector never polls the conntrack count unless --conntrack-every is set (off by default in every --api-mode). With the ceiling above, “how full is the connection table” is answerable from the container alone.

What it cannot tell you is what the connections are. /proc/slabinfo counts objects in a cache and nothing else: no protocol, no address, no state. A conntrack flood and a legitimate burst of many connections (a torrent, say) look the same in it. Without privileged=yes the file is unreadable and the count is gone; see what privileged buys. How to cross-check it once against the API is on conntrack without the API.

Mounting the host’s paths does not cross the boundary

Section titled “Mounting the host’s paths does not cross the boundary”

Whether bind-mounting the router’s own paths into the container gets past the namespaces was tested directly on the RB5009 on 2026-09-15, with the owner’s consent: a privileged container with host /proc, /sys and / mounted.

  • Host /proc mounts but reads zero PIDs. RouterOS renders a fresh procfs at the mount point, so the PID namespace holds and per-process CPU for RouterOS’s own processes stays out of reach.
  • Host /sys mounts but has no class/net. Sysfs networking is per network namespace and the mount does not carry the host’s, so per-interface counters stay API-only.
  • Host / works, and exposes the RouterOS flash filesystem: configuration and files, not live telemetry. A privileged container with / mounted reads the whole configuration, secrets included. mikroscope’s installer mounts nothing into the container and must never do this.

Namespaces are kernel boundaries, and a filesystem mount does not cross them.