Skip to content

Commands and flags

This page answers one question for each flag of mikroscope: what it does, what it defaults to, what range is accepted, and whether an environment variable can set it. It is read from cmd/mikroscope/*.go and internal/router/options.go, not from the help text, and where the two differ the page says so.

Terminal window
mikroscope <verb> [flags]

The verbs fall into five groups, each with its own flag set: the deployment verbs (doctor, plan, install, upgrade, uninstall, status, image), the recording verbs (record, mark, plot), the collector (forward), dashboards, and version. Flags are Go flag flags: -rate 50 and --rate 50 are the same, and a boolean is turned off with -privileged=false.

Status When
0 the verb finished
1 the verb ran and failed (a missing prerequisite, a router error, a sink that could not be built, a panel with no data), the verb is unknown, or a flag of record, mark, plot, forward or dashboards failed to parse or validate
2 no verb was given, or a flag of a deployment verb failed to parse or validate; nothing was sent to the router

Every flag is checked before the first connection, by Finish in internal/router/options.go, which runs for the deployment verbs and for record, mark, plot and forward alike. --triggers goes through the agent’s own ParseTriggers, so an unknown condition, a bad threshold, a quote or a semicolon fails the verb with exit 2 and nothing is sent. The agent parses TRIGGERS again when it starts, which is what catches an envlist edited by hand on the router: a bad value there shows up as a container that exits with status 2 after a mikroscope-agent: bad configuration: line in the router log, which restart-policy=on-failure then restarts.

A flag that names a variable in the tables below reads its default from MIKROSCOPE_<KEY>; a variable set to the empty string counts as unset, and a flag on the command line always wins. Only the flags that name a variable have one. --rate, --buffer, --port, --memory-max, --mem-limit-mb, --capture-mb, --triggers, --floor-hz, --privileged, --ephemeral and --expose are flags only. The CLI does not read .env itself; export it first with set -a; . ./.env; set +a. Environment variables lists every variable, including the credentials that have no flag at all.

Verb What it does Writes to the router
doctor Read-only preflight in one ssh connect; each failing check names its fix. Exits 1 if any prerequisite is missing. no
plan Prints every object install would create, then stops. Same as install --dry-run; with --rsc it writes a RouterOS script instead. no
install Gets the image, prints the listing, runs doctor, asks for confirmation, writes, then probes the agent from this host. yes
upgrade Gets a new image, checks that every install step is present, asks, removes and re-creates the container step, then probes. yes
uninstall Removes every step newest first, then verifies by ownership counts and fails naming anything that remains. yes
status Prints the ownership count of every step; if anything is installed, probes the agent and prints its health and board. no
image Builds the agent image tar and writes it to --out, for side-loading by hand. no

doctor runs these checks, in this order:

The checks doctor runs
Check, as printedPasses whenThe fix it names
registry-url is https://<host>with --remote-image, /container/config registry-url names the reference's registry host. Without --remote-image doctor does not ask: the setting is global to the device and mikroscope never writes it/container/config/set registry-url=https://<host> on the router, which applies to every container on it, or install from a tar with --agent-tar
container package installed and enableda container package exists with disabled=nodownload, upload, reboot; then /system/package/enable container
device-mode container=yes/system/device-mode reports container=yes/system/device-mode/update container=yes, then the reset or mode button, or a power cycle, within 5 minutes
architecture matches --arch <arch>the router's architecture-name is the one --arch maps to (arm64, arm, x86_64)re-run with the --arch it names
free memory ≥ <--memory-max>free-memory is at least what --memory-max asks for, 64 MiB by defaultfree memory on the router, or ask for less with --memory-max
free flash ≥ <size> (image tar + extracted root)without --disk: free-hdd-space is at least twice the image plus 4 MiBfree flash, or install with --disk tmpfs or --ephemeral where a tmpfs disk exists
disk <disk> existswith --disk or --ephemeral: a disk with that slot exists; its free space is not checked/disk/add type=tmpfs tmpfs-max-size=64M slot=tmpfs for a RAM disk, or name an existing disk with --disk
interface list <list> exists (raw rule trap)the --iface-list list (default LAN) exists/interface/list/add name=…, or pass the list your in-interface-list=!… drop rule uses
address list <list> has entries (raw rule trap)the --addr-list list (default LANs) has at least one entrypass the list your drop local if not from default IP range rule uses; an empty list is fine only if there is no such rule
veth name <veth> is free or oursalways reported ok, with the count foundnone: a collision is caught by install itself
Flag Default Variable Accepted Meaning
--router none, required MIKROSCOPE_ROUTER user@host or an ssh config alias ssh target; every verb but plan, install --dry-run and image fails without it
--ssh-port empty (ssh config) MIKROSCOPE_SSH_PORT ssh port
--ssh-key empty (agent or ssh config) MIKROSCOPE_SSH_KEY ssh identity file
--name mikroscope MIKROSCOPE_NAME ^[A-Za-z0-9][A-Za-z0-9_.-]{0,31}$ container name; tags every object as mikroscope:<name> (managed by mikroscope)
--veth veth-mikroscope MIKROSCOPE_VETH ^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$ veth interface name on the router
--subnet 172.30.10.0/30 MIKROSCOPE_SUBNET an IPv4 /30 at its network address the router takes .1, the agent .2
--iface-list LAN MIKROSCOPE_IFACE_LIST same pattern as --veth interface list the veth joins
--addr-list LANs MIKROSCOPE_ADDR_LIST same pattern as --veth address list the /30 joins
--disk empty (internal flash) MIKROSCOPE_DISK ^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$ RouterOS disk for the image tar and the root: tmpfs, disk1, usb1
--ephemeral false none forces --disk tmpfs and start-on-boot=no: nothing written to flash, nothing survives a reboot
--arch arm64 MIKROSCOPE_ARCH ^[a-z0-9]{1,16}$; doctor knows arm64, arm, amd64 device architecture, used as GOARCH and in the image manifest
--goarm 5 none GOARM level, used only with --arch arm: 5 runs on every 32-bit ARM MikroTik ships, 7 does not run on EN7562CT boards (hEX Refresh)
--agent-tar empty (build the agent here) MIKROSCOPE_AGENT_TAR a path to an agent image tar plan, install, upgrade, image: upload this tar instead of building one, so neither a Go toolchain nor a checkout is needed. The tar is checked first: one that is not a mikroscope agent image, or is built for an architecture other than --arch, fails the verb naming the asset to download
--remote-image empty (upload a tar) MIKROSCOPE_REMOTE_IMAGE a registry reference, owner/name:tag or host/owner/name:tag plan, install, upgrade: the router pulls the image itself, so nothing is built and nothing is uploaded, and no tar lands on the device. RouterOS takes the registry host from the global /container/config registry-url, which ships as https://registry-1.docker.io and which mikroscope never writes; doctor checks that setting against a reference that names a host of its own, such as the GHCR one, and names the command to run
--rsc false none plan: write a RouterOS script that installs from the router itself, instead of the listing
--port 9123 none 1–65535 agent HTTP port on the veth
--rate 10 none 1–100 sampler rate in Hz (envlist RATE_HZ); 10, 50 and 100 Hz measured lossless on the RB5009 (rate ceiling)
--buffer 300 none 10–3600 ring buffer in seconds (envlist BUFFER_S)
--memory-max 64M none ^\d{1,6}[KMG]?$ container cgroup memory-max, RouterOS syntax
--mem-limit-mb 40 none 8–1024 agent Go soft memory limit in MiB (envlist MEM_LIMIT_MB); must fit the ring, rate × buffer × about 2.4 kB, with room for the garbage collector
--capture-mb 4 none 0–256 triggered-capture budget in MiB (envlist CAPTURE_MB); 0 turns captures off
--triggers empty (the agent’s default set) none see triggered capture trigger conditions, comma-separated (envlist TRIGGERS)
--floor-hz 0 none 0–1000 one cadence for every level source, in Hz (envlist FLOOR_HZ); 0 keeps the per-source floors; equal to --rate reads and emits every source every tick
--privileged true none runs the container privileged=yes; -privileged=false opts out
--token empty MIKROSCOPE_TOKEN ^[A-Za-z0-9_.-]{0,128}$ bearer token the agent requires (envlist TOKEN); mandatory with --expose
--expose false none needs --lan-address and --token dst-nat the agent port on the router’s LAN address; adds two tagged firewall rules
--lan-address empty MIKROSCOPE_LAN_ADDRESS an IPv4 address the router’s LAN address for --expose
--dry-run false none install: print the listing and write nothing
--yes false none install, upgrade: do not ask before writing
--no-doctor false none install: skip the preflight checks
--out mikroscope-agent-<arch>.tar none image: output path of the tar. plan --rsc: where the script is written; empty writes it to standard output

Three settings of the container are not flags: logging=yes, restart-policy=on-failure with restart-max-count=5 and restart-interval=10s, and ignore-remote-image-change=yes. The restart values are the ones Defaults() sets; all three are what install writes (internal/router/steps.go). start-on-boot has no flag of its own either: it is no with --ephemeral and yes without.

Pass the same shape to status, upgrade and uninstall

Section titled “Pass the same shape to status, upgrade and uninstall”

status, upgrade and uninstall do not read what is on the router to learn how it was installed. Each one rebuilds the install plan from the flags of its own invocation and selects objects by that plan’s names, paths and tag. So:

  • An install made with --expose is only removed, and only verified, by an uninstall that is also given --expose --lan-address--token. Without them the two firewall rules are not in the plan, and the verification does not look for them.
  • upgrade removes the envlist with the container and writes it again from its own flags. An upgrade without the --rate, --buffer, --mem-limit-mb, --capture-mb, --triggers, --floor-hz or --token you installed with writes the defaults in their place, and --memory-max and --privileged likewise go back to theirs.
  • --name, --veth, --subnet, --iface-list, --addr-list, --port and --disk or --ephemeral decide what the selectors match; change one and the verb is looking for different objects.

What install writes to your router

  • a veth
  • one address
  • one interface-list membership
  • one address-list entry
  • an envlist
  • the image tar, unless --remote-image has the router pull the image
  • the container

Every object carries the comment mikroscope:<name> (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.

What install --expose adds

  • two firewall rules, tagged
  • a token becomes mandatory
  • uninstall and status see the two rules only when given --expose again

Every object carries the comment mikroscope:<name> (managed by mikroscope)

mikroscope plan prints every command before anything is written.

What upgrade replaces

  • a new image and the container
  • the envlist, rewritten from the flags upgrade is given
  • network objects stay

Every object carries the comment mikroscope:<name> (managed by mikroscope)

mikroscope plan prints every command before anything is written.

What uninstall removes

  • a veth
  • one address
  • one interface-list membership
  • one address-list entry
  • an envlist
  • the image tar, unless --remote-image has the router pull the image
  • the container

Every object carries the comment mikroscope:<name> (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.

The three verbs share one flag set with forward, so a flag that means something to a sibling is accepted and ignored: plot --for 5m parses and does nothing. The table marks which verb reads each flag.

Flag Default Variable Read by Meaning
--out capture-<UTC time> none record, mark output prefix: <out>.jsonl, .csv, .markers.csv, .meta.json. mark needs the prefix of an existing recording
--for 0 (until Ctrl-C) none record, forward run this long, then stop
--from-start false none record backfill everything the agent’s ring holds before going live
--poll 500ms none record, forward how often the agent’s ring is pulled
--batch 0 none record, forward samples per pull; 0 is twice what one --poll produces at the agent’s rate, never under 20. The relay caps a pull at 18. A pull repeats until it comes back short
--transport auto none record, forward auto (direct, then relay), direct (HTTP to the veth) or relay (/tool fetch over the RouterOS API; each call returns at most 64 512 B and took either ~3 ms or ~1 s on the RB5009, RouterOS 7.24.2, 2026-09-11; see reaching the agent)
--log-markers false none record, mark record: after recording, pull the router log over the API and append the matching lines to <out>.markers.csv. mark: add the log lines of the recording’s window
--topics system,interface,container none record, mark log topics kept as markers with --log-markers
--router-tz Local none record, mark IANA zone the router’s clock shows; RouterOS log times carry no zone
--in none, required none plot recording prefix, or its .jsonl path
--svg <in>.svg none plot output SVG
--title the prefix none plot chart title
--api empty MIKROSCOPE_API_ADDR record, mark, forward RouterOS API host:port, for the relay, --log-markers and the API tier
--api-user empty MIKROSCOPE_API_USER record, mark, forward API user; its password comes only from MIKROSCOPE_API_PASSWORD
--token empty MIKROSCOPE_TOKEN record, forward bearer token the direct transport sends to the agent
--port 9123 none record, forward agent HTTP port
--subnet 172.30.10.0/30 MIKROSCOPE_SUBNET record, forward the agent’s /30; its address is .2

mark takes the marker’s text as its remaining arguments, mikroscope mark --out cap "queue tree applied", or --log-markers instead of text. record turns every line typed on a terminal into a marker; when standard input is not a terminal it does not read it.

auto tries /healthz over the direct transport first. If that does not answer it needs --api, --api-user and MIKROSCOPE_API_PASSWORD to try the relay. It fails naming install --expose when the direct transport does not answer and the relay is not configured; a configured relay that fails reports relay transport: and its error. The relay does not carry the token: /tool fetch on the router sends no Authorization header, so an agent with a token set can only be pulled over the direct transport.

forward runs the collector: it pulls the kernel tier from the agent, polls the RouterOS API tier, and writes both to every sink named. It reads --for, --poll, --batch, --transport, --api, --api-user, --token, --port and --subnet from the table above, plus its own flags below. It refuses to start without at least one sink.

Flag Default Variable Meaning
--api-mode full none preset. off sets --api-every 0 unless you set it yourself, so the collector opens no API-tier session; the relay transport still uses the API. slow runs the tier every 10 s with no /system/health and no conntrack count; /system/resource, /system/resource/cpu, monitor-traffic on --interfaces and the --counters-every port counters are still read. full (the flag defaults) reads everything. An explicit flag below wins over it
--api-every 1s none API-tier cadence; 0 disables the tier. off sets it to 0, slow to 10s
--interfaces empty MIKROSCOPE_INTERFACES comma-separated interfaces for monitor-traffic, one call for all
--conntrack-every 0 none ask the conntrack count this often; 0 never, because it is a table scan. One scan took 1.3 ms at 6 212 entries on the RB5009 (date not recorded). slow sets it to 0 unless given explicitly
--counters-every 10s none read every port’s cumulative counters (typed errors, fast-path split, link-downs, frame sizes) this often; 0 never
--labels-every 5m none re-read what each interface is (label from its comment, type, interface lists, bridge, MTU); read once before the first kernel pull and again this often; 0 takes the 5 min default
--no-health false none skip /system/health. slow sets it

Without --api and --api-user, forward logs api tier disabled and runs the kernel tier alone; that is a warning, not a failure.

Flag Default Variable Meaning
--file empty none write the merged timeline as JSONL to this path (truncated at start)
--prom empty none serve Prometheus /metrics on this address, for example :9124
--influx empty MIKROSCOPE_INFLUX_URL InfluxDB 3 write URL, for example http://host:8181/api/v3/write_lp?db=mikroscope&precision=nanosecond
--loki empty MIKROSCOPE_LOKI_URL Loki push URL; carries events (kernel log, gaps, API errors, detections, triggers, the device record), not samples
--loki-tenant empty MIKROSCOPE_LOKI_TENANT X-Scope-OrgID for a multi-tenant Loki
--otlp empty MIKROSCOPE_OTLP_URL OTLP/HTTP metrics endpoint, for example http://host:4318/v1/metrics
--graphite empty MIKROSCOPE_GRAPHITE_ADDR Graphite carbon plaintext listener, host:port
--graphite-prefix mikroscope none first node of every Graphite metric path
--elastic empty MIKROSCOPE_ELASTIC_URL Elasticsearch or OpenSearch base URL for _bulk
--elastic-index mikroscope-%Y.%m.%d none index name; %Y, %m, %d expand to the event’s date
--sql empty none write PostgreSQL/TimescaleDB statements to this path, or to standard output with -; there is no database driver
--sql-hypertable false none also emit TimescaleDB create_hypertable calls in the SQL header
--telegraf empty MIKROSCOPE_TELEGRAF_URL Telegraf listener: http://host:8186/telegraf, tcp://host:8094 or udp://host:8094
--stdout empty none write to standard output as lp (InfluxDB line protocol) or json (NDJSON); any other value is refused
--host-tag router MIKROSCOPE_HOST_TAG host tag or label on every point, in every sink
--queue-seconds 60 none seconds of data each queued sink may hold before it drops the oldest batch; 0 or less takes 60

No sink credential is a flag, because a flag is visible in ps and in shell history: the tokens come from MIKROSCOPE_INFLUX_TOKEN, MIKROSCOPE_LOKI_TOKEN, MIKROSCOPE_OTLP_TOKEN, MIKROSCOPE_ELASTIC_AUTH and MIKROSCOPE_TELEGRAF_TOKEN. A sink that was asked for and cannot be built stops forward before it pulls anything.

On exit forward prints the number of kernel samples, API samples, gaps and skew jumps, and for each sink how many events it wrote, dropped and failed on. While it runs it logs a line on standard error once a minute with the kernel, API, gap, trigger and detection counts, the last sequence number, and each sink’s written, dropped and error counts.

The collector’s Prometheus sink sizes its busy-tick histogram for 10 Hz whatever rate the agent runs at (promHistogramRateHz in cmd/mikroscope/sinkflags.go), so the bucket layout does not change when it reconnects to an agent configured differently. Prometheus metric families says what else follows from that constant.

Terminal window
mikroscope dashboards gen
mikroscope dashboards import --store influxdb --datasource-uid <uid>
mikroscope dashboards check --store influxdb --datasource-uid <uid> --window 1h --end 2026-09-13T08:30:00Z
Flag Default Variable Read by Meaning
--out dashboards none gen output directory for mikroscope-<store>.json and mikroscope-alerts-<store>.yaml, both stores
--store influxdb none import, check influxdb or prometheus
--grafana empty GRAFANA_URL import, check Grafana base URL; the token comes only from GRAFANA_TOKEN
--datasource-uid empty, required none import, check the datasource UID bound to DS_MIKROSCOPE
--no-probe false none import, check do not ask the datasource which measurements it holds; use the compiled defaults
--window 15m none check length of the query window
--end now none check RFC 3339 instant the window ends at, to check against a capture that has already finished

import and check refuse to run without --grafana (or GRAFANA_URL), GRAFANA_TOKEN and --datasource-uid. Unless --no-probe is given, they first ask the datasource which measurements it holds; if that question fails they warn and continue with the compiled defaults. check prints one line per panel (ok, none for a known-empty panel, FAIL) and exits 1 if any panel fails. The variables are GRAFANA_URL and GRAFANA_TOKEN, without the MIKROSCOPE_ prefix.

mikroscope version prints mikroscope and the build identity from internal/version. It takes no flags.