Environment variables
Two programs read environment variables, and they read different ones. The
CLI on your machine reads MIKROSCOPE_* variables as flag defaults, plus a
handful of credentials that have no flag, plus GRAFANA_URL and
GRAFANA_TOKEN. The agent on the router reads unprefixed variables
(RATE_HZ, TOKEN …) from its container envlist, which install writes. The
CLI’s are defined in cmd/mikroscope/, the agent’s
in internal/agent/config.go.
Precedence
Section titled “Precedence”-
A variable sets a flag’s default; the flag on the command line wins.
-
A variable set to the empty string is treated as unset.
-
The shape variables (
MIKROSCOPE_VETH,MIKROSCOPE_SUBNET,MIKROSCOPE_IFACE_LIST,MIKROSCOPE_ADDR_LIST,MIKROSCOPE_DISK,MIKROSCOPE_LAN_ADDRESS,MIKROSCOPE_REMOTE_IMAGE) set whatinstallwrites.status,upgradeanduninstalltake the shape of the install on the router instead, from its manifest or its tagged objects, and refuse a variable or flag that contradicts it, naming both values;MIKROSCOPE_REMOTE_IMAGEis the exception, filled in forstatusanduninstalland never refused..env.exampleleaves them unset for that reason. -
The CLI never reads a
.envfile. Export it into the shell first:Terminal window set -a; . ./.env; set +a -
Quote a URL that holds
&when it lives in a file yousource: unquoted,&is a shell operator..env.examplequotesMIKROSCOPE_INFLUX_URLfor that reason.
Most deployment flags have no variable. --rate, --buffer, --port,
--memory-max, --mem-limit-mb, --capture-mb, --triggers, --floor-hz,
--privileged, --ephemeral, --expose, --restart-max-count,
--restart-interval, --start-on-boot, --container-name and
--extract-timeout are set on the command line or not at all. CLI has every flag.
Router and deployment
Section titled “Router and deployment”| Variable | Flag | Read by | Default in the code | Value in .env.example |
|---|---|---|---|---|
MIKROSCOPE_ROUTER |
--router |
doctor, install, upgrade, uninstall, status |
none (required) | set |
MIKROSCOPE_SSH_PORT |
--ssh-port |
the same | ssh config | 22 |
MIKROSCOPE_SSH_KEY |
--ssh-key |
the same | ssh agent or config | empty |
MIKROSCOPE_SSH_OPTIONS |
--ssh-option |
the same | none | commented out, StrictHostKeyChecking=accept-new |
MIKROSCOPE_ARCH |
--arch |
the deployment verbs | auto |
commented out, auto |
MIKROSCOPE_AGENT_TAR |
--agent-tar |
doctor, plan, install, upgrade, image |
empty (build the agent here) | commented out |
MIKROSCOPE_REMOTE_IMAGE |
--remote-image |
the deployment verbs | empty (upload a tar) | commented out |
MIKROSCOPE_NAME |
--name |
the deployment verbs | mikroscope |
commented out |
MIKROSCOPE_VETH |
--veth |
the deployment verbs | veth-mikroscope |
commented out |
MIKROSCOPE_SUBNET |
--subnet |
the deployment verbs, record, forward |
172.30.10.0/30 |
commented out |
MIKROSCOPE_IFACE_LIST |
--iface-list |
the deployment verbs | LAN; none joins no list |
commented out |
MIKROSCOPE_ADDR_LIST |
--addr-list |
the deployment verbs | LANs; none joins no list |
commented out |
MIKROSCOPE_DISK |
--disk |
the deployment verbs | empty (internal flash) | commented out, tmpfs |
MIKROSCOPE_LAN_ADDRESS |
--lan-address |
the deployment verbs | empty | commented out |
MIKROSCOPE_TOKEN |
--token |
the deployment verbs, record, forward |
empty | commented out |
Scroll sideways to see every column
MIKROSCOPE_SSH_OPTIONS is a comma-separated list of Key=value pairs, the
same as repeating --ssh-option; the first --ssh-option on the command line
replaces the whole list. CLI has
the keys it takes.
MIKROSCOPE_TOKEN is two things at once. For install and upgrade it is the
token written into the agent’s envlist, which the agent then requires; the CLI
hands the command that writes it to ssh on its standard input, never on a
command line. status and uninstall do not need it. For record,
forward and doctor’s health section it is the token sent to the agent over
the direct transport; without it, doctor skips the ring of an agent that has
one. The envlist is not
a secret store: any RouterOS user with the read policy can list every
container’s envlist over the API, which is why the token is the only
credential that goes there.
Agent and API access
Section titled “Agent and API access”| Variable | Flag | Read by | Meaning |
|---|---|---|---|
MIKROSCOPE_API_ADDR |
--api |
record, mark, forward |
RouterOS binary API host:port; .env.example has 192.168.88.1:8728 |
MIKROSCOPE_API_USER |
--api-user |
record, mark, forward |
the dedicated API user |
MIKROSCOPE_API_PASSWORD |
none | record, mark, forward |
that user’s password; there is deliberately no flag |
MIKROSCOPE_INTERFACES |
--interfaces |
forward |
comma-separated interfaces for monitor-traffic; .env.example has bridge,ether1 commented out |
Scroll sideways to see every column
The relay, --log-markers and the API tier fail, or in forward’s case are
disabled with a warning, unless --api, --api-user and
MIKROSCOPE_API_PASSWORD are all set. The API
user has the policy that user needs.
| Variable | Flag | Default in the code | Meaning |
|---|---|---|---|
MIKROSCOPE_INFLUX_URL |
--influx |
empty | InfluxDB 3 server, http://host:8181; a full write URL is still taken verbatim |
MIKROSCOPE_INFLUX_DB |
--influx-db |
empty | the database --influx writes to, when --influx is a bare server |
MIKROSCOPE_POSTGRES_DSN |
--postgres |
empty | PostgreSQL connection string for the connecting SQL sink |
MIKROSCOPE_LOKI_URL |
--loki |
empty | Loki push URL |
MIKROSCOPE_LOKI_TENANT |
--loki-tenant |
empty | X-Scope-OrgID |
MIKROSCOPE_OTLP_URL |
--otlp |
empty | OTLP/HTTP metrics endpoint |
MIKROSCOPE_GRAPHITE_ADDR |
--graphite |
empty | carbon plaintext host:port |
MIKROSCOPE_ELASTIC_URL |
--elastic |
empty | Elasticsearch or OpenSearch base URL |
MIKROSCOPE_TELEGRAF_URL |
--telegraf |
empty | Telegraf listener URL (http://, tcp:// or udp://) |
MIKROSCOPE_HOST_TAG |
--host-tag |
router |
host tag on every point in every sink; .env.example has it commented out |
Scroll sideways to see every column
Setting a sink’s URL variable is enough to turn that sink on: forward builds
every sink whose flag ends up non-empty, from the command line or from the
variable. Every one of these names carries the MIKROSCOPE_ prefix: a bare
LOKI_URL is read by nothing.
Credential variables
Section titled “Credential variables”A flag is visible in ps and in a shell history, so every secret the CLI uses
is read from the environment only.
| Variable | Used for |
|---|---|
MIKROSCOPE_API_PASSWORD |
the RouterOS API user |
MIKROSCOPE_INFLUX_TOKEN |
InfluxDB, sent as Authorization: Bearer <token> |
MIKROSCOPE_LOKI_TOKEN |
Loki |
MIKROSCOPE_OTLP_TOKEN |
the OTLP endpoint |
MIKROSCOPE_ELASTIC_AUTH |
Elasticsearch or OpenSearch |
MIKROSCOPE_ |
the Telegraf HTTP listener |
GRAFANA_TOKEN |
dashboards publish, import and check, forward --grafana, uninstall --targets dashboard |
Scroll sideways to see every column
Grafana
Section titled “Grafana”GRAFANA_TOKEN is the one Grafana token every verb uses, with no prefix, and no flag takes it.
The Grafana itself is --grafana, and each verb looks for a default in a different order:
| Verb | Reads, after --grafana |
|---|---|
dashboards publish, uninstall --targets dashboard |
MIKROSCOPE_GRAFANA_URL, then GRAFANA_URL |
dashboards import, dashboards check |
GRAFANA_URL, then MIKROSCOPE_GRAFANA_URL |
forward |
MIKROSCOPE_GRAFANA_URL only |
Scroll sideways to see every column
forward never reads GRAFANA_URL, so a GRAFANA_URL on its own does not make the collector
publish. Other Grafana tools read that name and GRAFANA_TOKEN too, and a collector started from a
shell set up for one of them would start writing a folder, a datasource and a dashboard into that
Grafana without anyone asking it to. The file .env.example carries every Grafana
variable, commented out, in its two Grafana blocks.
forward, dashboards publish and uninstall take --grafana and the flags below, and their
variables do carry the prefix, because they are collector settings rather than the
dashboards import and check ones:
| Variable | Flag | Default in the code | Meaning |
|---|---|---|---|
MIKROSCOPE_GRAFANA_URL |
--grafana |
empty | the Grafana to publish to; empty publishes nothing |
MIKROSCOPE_ |
--grafana-folder |
mikroscope |
folder to publish into; an empty variable is ignored, so the General folder takes --grafana-folder "" |
MIKROSCOPE_ |
--grafana-datasource-uid |
empty | adopt this datasource instead of creating one; one store per run |
MIKROSCOPE_ |
--grafana-datasource-url |
empty | the address Grafana queries, for the sinks that cannot know it; one store per run |
MIKROSCOPE_ |
--grafana-datasource-sslmode |
empty | sslmode for the PostgreSQL datasource: disable, require, verify-ca or verify-full, nothing else |
Scroll sideways to see every column
uninstall reads only MIKROSCOPE_GRAFANA_URL and MIKROSCOPE_ of these,
and ignores the others.
A run that publishes more than one store and sets MIKROSCOPE_ or
MIKROSCOPE_ is refused before any request: each names one datasource.
Publish the store that needs one on its own with dashboards publish (Grafana
publishing).
Publishing refuses to write without GRAFANA_TOKEN: some Grafanas accept an anonymous request, and
one that did would write as whoever the server thinks is asking. --grafana-dry-run writes nothing
and sends no request, so it runs without the token.
Agent envlist
Section titled “Agent envlist”The agent reads these from its container’s environment when it starts. An
invalid value makes it print one line starting
mikroscope-agent: bad configuration:, which RouterOS puts in its log under
the container topic, and exit with status 2.
| Variable | Agent default | Accepted | Meaning |
|---|---|---|---|
RATE_HZ |
10 |
1–100 | sampler rate |
BUFFER_S |
60 |
10–3600 | ring length in seconds |
PORT |
9123 |
1–65535 | HTTP port |
ADDR |
empty | an address | bind address. Empty binds :PORT, every address in the container’s network namespace; install always sets it to the agent’s veth address |
TOKEN |
empty | when set, every path but /healthz requires Authorization: Bearer <token> |
|
IRQ_TOP_K |
8 |
0–64 | interrupt lines kept per sample, busiest first; 0 ships no interrupt lines and no irq_total |
MEM_LIMIT_MB |
14 |
8–1024 | Go soft memory limit in MiB |
FLOOR_HZ |
0 |
0–1000 | 0 keeps the per-source floors; N puts every level source on N Hz and turns off emit-on-change |
SOURCES |
empty (every source detected) | comma-separated names | restrict the optional sources to this list; /proc/stat is always read |
TRIGGERS |
softnet-drop, |
conditions, comma-separated | capture trigger conditions |
CAPTURE_MB |
4 |
0–256 | pinned-bytes budget for captures; 0 turns them off |
CAPTURE_PRE_S |
5 |
1–60 | seconds kept before the sample a trigger fires on |
CAPTURE_POST_S |
5 |
1–60 | seconds kept after it |
CAPTURE_POLICY |
first |
first or last |
on a full budget: first refuses the new capture, last evicts the oldest |
TRIGGER_REFRACTORY_S |
10 |
0–3600 | quiet time per condition after it fires |
PROC_ROOT |
/proc |
a path | where /proc is read from; the kernel log is /dev/kmsg only when this is /proc, otherwise dev/kmsg beside the given directory |
SYS_ROOT |
/sys |
a path | where /sys is read from |
Scroll sideways to see every column
The names SOURCES accepts are the optional sources /capabilities reports:
meminfo, loadavg, softnet, softirqs, interrupts, vmstat, psi,
schedstat, self, buddyinfo, yaffs, diskstats, slabinfo, kmsg,
thermal, cpufreq, mtd and perf. The trigger conditions and what each
compares are on Triggered capture.
Before it listens, the agent checks the ring against the container’s own
memory.max: RATE_HZ × BUFFER_S × 3 456 B plus CAPTURE_MB must fit, or it
refuses to start and names the three variables and --memory-max. If that
figure is more than half of MEM_LIMIT_MB, it starts and logs a warning with
the limit to raise to. 3 456 B is not itself a measurement: it is the Go
allocator size class that serves a line with every source on
(3 230 B, 4 cores, IRQ_TOP_K 8), because that class
is what the heap is charged. A board with more cores or more interrupt lines
writes longer lines, so the check errs open. When the
agent cannot read its memory.max, the refusal check does not run.
Written by install
Section titled “Written by install”install and upgrade write the envlist <name>-env with these entries, in
this order, and nothing else. The install’s shape (its lists, disk and
exposure) is not in the envlist: install records it in the install manifest,
mikroscope/ on the install’s disk, which holds no secret
either.
| Key | Written | From | Holds |
|---|---|---|---|
MIKROSCOPE_TAG | always | --name | the ownership marker mikroscope:<name> (managed by mikroscope), written first and removed last; the agent ignores it |
RATE_HZ | always | --rate, default 10, 1–100 | the sampler rate, in Hz |
BUFFER_S | always | --buffer, default 60, 10–3600 | the ring's length, in seconds |
PORT | always | --port, default 9123, 1–65535 | the agent's HTTP port |
ADDR | always | --subnet | the agent's address, the .2 of the /30; the agent binds only there |
MEM_LIMIT_MB | always | --mem-limit-mb, 8–1024 | the agent's Go soft memory limit, in MiB; derived from the ring (rate × buffer × line, × 2.5, at least 16 MiB, at most three quarters of --memory-max while that still holds the ring) unless --mem-limit-mb gives it |
FLOOR_HZ | only when above 0 | --floor-hz, default 0, 0–1000 | one cadence for every level source, in Hz |
CAPTURE_MB | always | --capture-mb, default 4, 0–256 | the triggered-capture budget, in MiB; 0 turns captures off |
TRIGGERS | only when set | --triggers | the trigger conditions; unset, the agent uses its default set |
TOKEN | only when set | --token | the bearer token the agent requires, from --token or MIKROSCOPE_TOKEN, with or without --expose |
Scroll sideways to see every column