# mikroscope documentation: the core > mikroscope is open source under the MIT licence and written in Go: an agent that runs in a container on MikroTik RouterOS 7.24 or later (arm64, arm, x86_64), and a CLI that installs it, records, plots and forwards to eleven sinks. Current release: 1.6.1, 2026-10-06. 11 pages, in the order the site's sidebar lists them, separated by `---`. Each opens with its title, its description and the URL of the page it doubles. The index of every file: https://jmrplens.github.io/mikroscope/llms.txt --- # mikroscope Sub-second kernel telemetry from inside a MikroTik router, with the cost of the observer measured rather than claimed. Source: https://jmrplens.github.io/mikroscope/ The RouterOS API reports CPU load once a second. mikroscope runs on the router, in a container, and reads the kernel's own counters at 1 to 100 Hz — for whoever has to say what a MikroTik device did inside that second. Two MIT-licensed binaries, and what the observer costs the router measured rather than promised, first thing below. ## Measured, not budgeted - [**2.69 %** — of one core at 10 Hz, the install default](https://jmrplens.github.io/mikroscope/about/status/#campaign-rates-2026-09-18) - [**13.2 MiB** — resident memory at 10 Hz](https://jmrplens.github.io/mikroscope/about/status/#campaign-rates-2026-09-18) - [**16.83 %** — of one core at 100 Hz, the CLI's cap](https://jmrplens.github.io/mikroscope/about/status/#campaign-rates-2026-09-18) - [**0 / 0** — gaps and drops, across six runs](https://jmrplens.github.io/mikroscope/about/status/#campaign-rates-2026-09-18) The first three are from the agent's own cgroup, carried on every sample; the fourth is from InfluxDB 3, the one sink the collector forwarded to. Each of the first three is one 300 s window at steady state, not repeated, so none has a spread. At 10 Hz the memory is inside the ≤ 16 MiB the budget asks for and the CPU is above the ≤ 2 %. The router, the RouterOS version and the date are on [Tested on](https://jmrplens.github.io/mikroscope/about/status/#campaign-rates-2026-09-18). ## A one-second average is a report about a second The RouterOS API reports `cpu-load` once a second. A core saturated for 100 ms and idle for the other 900 moves a four-core, one-second average by 2.5 %. That is arithmetic, not a measurement, and the figure is true: it just cannot say when. The agent reads `/proc/stat`, `/proc/interrupts`, `/proc/softirqs` and `/proc/net/softnet_stat` from inside the router, at 10 Hz by default, and ships raw tick deltas with the interval each one covers. It never computes a percentage; the window is yours. The floor is the kernel's, not the tool's. `/proc/stat` counts in ticks of 10 ms, so a 100 ms sample resolves one core to 10 % steps. There is nothing finer to read where the kernel has no PSI and no schedstat, and the tested router has neither ([Tested on](https://jmrplens.github.io/mikroscope/about/status/#campaign-kernel-2026-09-11)). [How many seconds `cpu-load` averages over →](https://jmrplens.github.io/mikroscope/sinks/api-tier/#how-many-seconds-does-routeros-cpu-load-average-over) ## What it costs, at three rates Conditions: 300 s windows at steady state (ring full), full source set, the shipped configuration — a 60 s ring, the memory limit derived from it and the default 64M container cap — with the collector forwarding to InfluxDB 3. Each row is one window, not repeated, so no row has a spread; memory differs by row because the ring and the memory limit do. The measured runs: | rate | floors | CPU of one core | µs/sample | RSS | slipped ticks | gaps / drops | | --- | --- | --- | --- | --- | --- | --- | | 10 Hz (default) | default | **2.69 %** | 2 685 | 13.2 MiB | **0** | 0 / 0 | | 50 Hz | default | **9.63 %** | 1 926 | 23.3 MiB | **0** | 0 / 0 | | 100 Hz | default | **16.83 %** | 1 684 | 45.7 MiB | 5 (0.02 %) | 0 / 0 | Nothing was lost at any of these rates: every sink reported 0 gaps and 0 drops, and the delivered rate matched the configured one to three figures. At the default floors and 100 Hz, a whole tick's sources were read in under 2 ms for 97.7 % of samples, inside a 10 ms period. [All six runs, including every source on every tick →](https://jmrplens.github.io/mikroscope/cost/rate-ceiling/) ## The router's CPU from the kernel, its interfaces from the API ### Kernel tier · the agent · 10 to 100 Hz Global inside the container, so these are the router's own: per-core CPU ticks, interrupts, softirqs, softnet drops and time squeezes, `/proc/meminfo`, `/proc/vmstat`, load and disk I/O. A privileged container adds the kernel log as timestamped events and the global slab caches. ### API tier · the collector · 1 Hz The container has its own network namespace, so `/proc/net/dev` describes the container, not the router. Interface bytes and packets come from the RouterOS API and are merged by the collector, not interpolated. `privileged=yes` does not change that. ## Every write listed before it is made Current release: [1.6.1](https://github.com/jmrplens/mikroscope/releases/tag/v1.6.1), 2026-10-06 · [Changelog](https://github.com/jmrplens/mikroscope/blob/main/CHANGELOG.md) Download the archive for your platform from the release, or build the CLI from a checkout with `make build`. The router needs RouterOS 7.24 or later — the container step writes `privileged=`, an attribute earlier 7.x releases reject — with the `container` package and `device-mode container=yes`, which MikroTik gates behind a reset-button press or a power cycle. arm64, arm and x86_64; not MIPS, not TILE. 1. `mikroscope doctor` Read-only preflight that names the fix for anything missing, then what the running agent's ring shows: a layer-2 loop, STP churn, a link flap or softnet drops. 2. `mikroscope plan` Every RouterOS command, nothing written. 3. `mikroscope install` Doctor, confirmation, the writes, then a probe of the agent. The image comes from your own Go toolchain, from the published agent tar, or from the registry the router pulls it from. 4. `mikroscope status` Ownership counts and the agent's health. 5. `mikroscope uninstall` Removes and verifies. **What `install` writes to your router** - the install manifest, a file `mikroscope/.manifest.txt` on the install's disk that lists the options and every object below - a veth - one address - one interface-list membership, unless `--iface-list none` - one address-list entry, unless `--addr-list none` - an envlist - the image tar, deleted once the container is extracted, unless `--remote-image` has the router pull the image - the container, and its root `mikroscope/` on the same disk Every object carries the comment `mikroscope: (managed by mikroscope)` `mikroscope plan` prints every command before anything is written. `uninstall` removes by exact tag plus identity, never by pattern, and fails naming the step if anything remains. [Quick install, step by step →](https://jmrplens.github.io/mikroscope/start/) ## Tested on The figures on this page come from one router. [Tested on](https://jmrplens.github.io/mikroscope/about/status/) names it, with the RouterOS versions, the dates and the conditions of every run. It also lists what has not been tested: other boards, traffic heavier than that router's ordinary load, and the sinks and builds that have not run on hardware. No rate is claimed for any other board. Cost scales with core speed, source set and ring size. [Measure it on your own router](https://jmrplens.github.io/mikroscope/cost/#measuring-it-on-your-own-device) before you budget for it. ## Where to go next - [Quick install](https://jmrplens.github.io/mikroscope/start/): Install the CLI, check the router and run the agent in a RouterOS container - [How it works](https://jmrplens.github.io/mikroscope/how-it-works/): The agent, the collector and the data path between them, what each reads and adds, and what the tool does not do - [Compared with alternatives](https://jmrplens.github.io/mikroscope/start/compared/): The other ways to watch a RouterOS device, what each one reads, and when to use another tool - [First recording](https://jmrplens.github.io/mikroscope/start/walkthrough/): Record while you change something, mark it, and draw the chart - [Install methods](https://jmrplens.github.io/mikroscope/install/routes/): Every way to get the agent onto a router, and what each one needs - [Run the collector](https://jmrplens.github.io/mikroscope/sinks/): Pull, merge, derive, fan out to eleven sinks, and why a slow one never stops the loop - [Diagnose faults](https://jmrplens.github.io/mikroscope/playbooks/): What each fault looks like in the kernel and port data, and the checks to make before you trust a reading - [Limits of the evidence](https://jmrplens.github.io/mikroscope/cost/limits/): Every limit on the figures above --- # Quick install Install the CLI, check the router and run the agent in a RouterOS container, in four commands. Source: https://jmrplens.github.io/mikroscope/start/ mikroscope runs an agent in a RouterOS container that samples the router's kernel at 10 Hz and answers over HTTP on the router's side of a /30. The CLI on your computer installs it, and `install` writes these objects, which `uninstall` removes: **What `install` writes to your router** - the install manifest, a file `mikroscope/.manifest.txt` on the install's disk that lists the options and every object below - a veth - one address - one interface-list membership, unless `--iface-list none` - one address-list entry, unless `--addr-list none` - an envlist - the image tar, deleted once the container is extracted, unless `--remote-image` has the router pull the image - the container, and its root `mikroscope/` on the same disk Every object carries the comment `mikroscope: (managed by mikroscope)` `mikroscope plan` prints every command before anything is written. `uninstall` removes by exact tag plus identity, never by pattern, and fails naming the step if anything remains. `install` writes nothing outside the router: the collector and the Grafana dashboards are a separate, optional step ([See it in Grafana](https://jmrplens.github.io/mikroscope/start/#see-it-in-grafana)). ## Requirements | Need | Value | | ------------------- | --------------------------------------------------------------- | | RouterOS | 7.24 or later | | Architecture | `arm64`, `arm` or `x86_64`; not MIPS, TILE or PPC | | `container` package | installed and enabled | | Device mode | `container=yes`, confirmed with a button press or a power cycle | You also need a computer with ssh access to the router as an admin user, with a key or an ssh agent: the CLI runs ssh in batch mode and cannot answer a password prompt. [Requirements](https://jmrplens.github.io/mikroscope/install/prerequisites/) has the details and what `doctor` checks. ## Install the CLI ```sh curl -fsSL https://raw.githubusercontent.com/jmrplens/mikroscope/main/install.sh | bash ``` In PowerShell on Windows: ```powershell irm https://raw.githubusercontent.com/jmrplens/mikroscope/main/install.ps1 | iex ``` The script installs the newest release and refuses an archive whose SHA-256 is not the one the release published. [Install the CLI](https://jmrplens.github.io/mikroscope/install/cli/) has the same steps by hand. ## Check the router ```sh mikroscope doctor --router admin@192.168.88.1 --remote-image jmrplens/mikroscope-agent:1.6.1 ``` `doctor` reads the router and writes nothing. It prints one line per check, marked `ok`, `MISSING` or `WARN`, and ends with `doctor: every prerequisite is met`. A `MISSING` line is followed by its fix: ```text MISSING interface list LAN exists (found=0) fix: --iface-list none: no firewall rule here needs the veth in an interface list. Or create it: `/interface/list/add name=LAN` ``` Apply the fix, or add the flag it names to this command and to `install` below, and run `doctor` again until nothing is `MISSING`. A `WARN` line does not stop the install. > **Device mode needs you at the router** > > After `/system/device-mode/update container=yes`, press the reset or mode button, or cut the > power, within 5 minutes. On CHR, power the virtual machine off and on. No ssh session or API call > can do this step. > **A router ssh has never seen** > > The CLI runs ssh in batch mode, so a host key that is not in `known_hosts` yet stops it. Add > `--ssh-option StrictHostKeyChecking=accept-new` to the first command, or connect once with `ssh` > yourself. ## Install the agent ```sh mikroscope install --router admin@192.168.88.1 --remote-image jmrplens/mikroscope-agent:1.6.1 ``` `install` runs the same checks, reads the router's architecture, and prints every RouterOS command it will run, ending with `nothing above has been written yet`. It then asks `write the objects above to the router? [y/N]`. After the writes it probes the agent from your computer: ```text install done: 6 step(s) created probing http://172.30.10.2:9123/healthz from this host … direct transport ok: agent 1.6.1 () built