# Storage and container settings

Where install puts the image, the container root and the install manifest, the container's settings and the flags that change them, and how to size its memory.

Source: https://docs.jmrp.io/mikroscope/install/layout/

`install` puts the image tar, the container root and the install manifest on one disk, and creates
the container with the settings below. Run `mikroscope plan` with your flags to see every value
before anything is written.

## Defaults

| Object           | Default                                                                                                              | Flag                                                   |
| ---------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| veth + /30       | `veth-mikroscope`, `172.30.10.0/30` (router `.1`, agent `.2`)                                                        | `--veth`, `--subnet`                                   |
| router address   | `172.30.10.1/30` on the veth                                                                                         | derived from `--subnet`                                |
| install manifest | `mikroscope/<name>.manifest.txt` on the internal flash                                                               | `--disk`, `--ephemeral`                                |
| image tar        | `<name>.tar`, uploaded with `scp` and deleted once the image is extracted; none with `--remote-image`                | `--disk`, `--ephemeral`, `--remote-image`              |
| container root   | `mikroscope/<name>` on the internal flash                                                                            | `--disk`, `--ephemeral`                                |
| container        | no `name=`: RouterOS names it                                                                                        | `--container-name`                                     |
| envlist          | `<name>-env`                                                                                                         | `--rate`, `--buffer`, `--port`, `--token`, …           |
| tag              | `mikroscope:<name> (managed by mikroscope)` on every object that takes a comment; as `MIKROSCOPE_TAG` in the envlist | `--name`, default `mikroscope`                         |

- `--subnet` is an IPv4 /30 given at its network address; the router takes `.1` and the agent `.2`.
  If `172.30.10.0/30` is in use on your router, pick another: `doctor` names a route that overlaps
  it.
- `--name` and `--container-name` take up to 32 characters and `--veth` up to 64: letters, digits,
  `_`, `.` and `-`, starting with a letter or digit.
- `--disk` is a RouterOS disk slot: empty for the internal flash, `tmpfs`, `disk1`, `usb1` and so
  on. With `--disk`, the manifest, the tar and the root move together: `<disk>/<name>.tar` and
  `<disk>/mikroscope/<name>`.
- Every removal selects the container by its tag, not by its name.

`doctor` checks the space and the disk before `install` writes:

The checks doctor runs:

| Check, as printed | Passes when | The fix it names |
| --- | --- | --- |
| free flash ≥ <size> (image tar + extracted root) | without `--disk` or `--ephemeral`: `free-hdd-space` is at least twice the image plus 4 MiB. With `--remote-image` nothing is uploaded and the name ends in `(extracted root)`: the root the pulled image is extracted into, 7 MiB, plus 4 MiB | free flash, or install with `--disk tmpfs` or `--ephemeral` where a tmpfs disk exists |
| disk <disk> exists | with `--disk` or `--ephemeral`: a disk with that slot exists | `/disk/add type=tmpfs tmpfs-max-size=64M slot=tmpfs` for a RAM disk, or name an existing disk with `--disk` |
| disk <disk> has ≥ <size> free (image tar + extracted root) | with `--disk` or `--ephemeral`, once the disk exists: its free space is at least twice the image plus 4 MiB; with `--remote-image`, the 7 MiB root plus 4 MiB, as the flash check | free space on that disk, or give a tmpfs disk a larger `tmpfs-max-size` |
| disk tmpfs is RAM | with `--ephemeral`: the disk in slot `tmpfs` is of type `tmpfs` | free the slot for a tmpfs disk, or install with `--disk <slot>` without `--ephemeral` |
| start-on-boot suits a root in RAM | a warning, when the disk is a tmpfs disk: start-on-boot resolves to `no`, since a reboot empties the disk and a container started at boot has no root | pass `--start-on-boot no`, or `--ephemeral` |

## Persistent or ephemeral

|                        | Default                | `--disk tmpfs`                         | `--ephemeral`                       |
| ---------------------- | ---------------------- | -------------------------------------- | ----------------------------------- |
| Manifest, tar and root | internal flash         | the tmpfs disk                         | the tmpfs disk                      |
| `start-on-boot`        | `yes`                  | `yes`; `doctor` warns                  | `no`                                |
| Flash writes           | the tar and the root   | none for the tar and the root          | none                                |
| After a reboot         | the agent starts again | the root is gone, and a container started at boot has none | the container is stopped, with its root, image and manifest gone |

- **Persistent is the default**: the root on the internal flash, `start-on-boot=yes`, and
  `restart-policy=on-failure` bounded to five restarts ten seconds apart, so a broken image cannot
  loop at boot.
- **`--ephemeral`** puts the manifest, the tar and the root on the disk in slot `tmpfs`, which must
  be a RAM disk, and sets `start-on-boot=no`. Nothing is written to flash
  ([measured](https://docs.jmrp.io/mikroscope/about/status/#verified-tmpfs-zero-writes)), and nothing survives a reboot. A router
  without that disk fails `doctor`, whose fix adds one:

  ```routeros
  /disk/add type=tmpfs tmpfs-max-size=64M slot=tmpfs
  ```

- **`--disk tmpfs`** without `--ephemeral` puts the same files on the tmpfs disk and keeps
  `start-on-boot=yes`, which `doctor` warns about. Add `--start-on-boot no`, or use `--ephemeral`.

After a reboot, an `--ephemeral` install's container is still configured and stopped, and its
manifest went with the disk ([tested](https://docs.jmrp.io/mikroscope/about/status/#virtual-lab)).
`uninstall --ephemeral` removes the rest by its tag.

## Container settings

| Setting                      | Value                                                                  | Flag                                               |
| ---------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------- |
| `name=`                      | not written, so RouterOS names the container                           | `--container-name`                                 |
| `file=` or `remote-image=`   | the uploaded tar, or the whole reference, registry host included       | `--agent-tar`, `--remote-image`                    |
| `interface=`                 | the veth                                                               | `--veth`                                           |
| `root-dir=`                  | `mikroscope/<name>` on the disk                                        | `--disk`, `--ephemeral`, `--name`                  |
| `envlist=`                   | `<name>-env`                                                           | `--name`                                           |
| `logging=`                   | `yes`, so what the agent prints reaches the router log                 | none                                               |
| `start-on-boot=`             | `yes`; `no` with `--ephemeral`                                         | `--start-on-boot`: `auto`, `yes` or `no`          |
| `restart-policy=`            | `on-failure`                                                           | none                                               |
| `restart-max-count=`         | `5`                                                                    | `--restart-max-count`, 0–100                       |
| `restart-interval=`          | `10s`                                                                  | `--restart-interval`, `^\d{1,4}[smh]$`             |
| `memory-max=`                | `64M`, enforced as the container's cgroup limit                        | `--memory-max`                                     |
| `privileged=`                | `yes`                                                                  | `--privileged=false`                               |
| `ignore-remote-image-change=` | `yes`                                                                 | none                                               |
| `comment=`                   | the tag                                                                | `--name`                                           |

- `--start-on-boot auto` is `no` with `--ephemeral` and `yes` otherwise.
- `privileged=yes` drops the container's user namespace, so the kernel log, `/proc/slabinfo` and
  the MTD ECC counters are readable. It does not widen the network or PID namespace; [Privileged
  mode](https://docs.jmrp.io/mikroscope/limits/privileged/) has the detail.
- The agent catches SIGTERM; RouterOS kills a container that does not, at once. The agent stops
  within RouterOS's default stop time of 10 s.

## Image tar cleanup

RouterOS extracts the image when the container is added, and the tar has no use after that. On the
tar route, `install`:

1. adds the container;
2. waits until RouterOS reports it `stopped`, which is when the extraction is done, for up to
   `--extract-timeout` (default `120s`, 10–600 s);
3. deletes the tar;
4. starts the container.

On timeout it stops with `mikroscope: the image was not extracted within 120 s; mikroscope.tar
stays`. The tar stays with the container, and `uninstall` removes the two together. A tar left
behind would be what `uninstall` has to find later, in a `/file` index that can lag a container
removal by minutes.

The container is created with `ignore-remote-image-change=yes`. With `no`, RouterOS watches the
image, and removing the tar makes it stop, remove and re-extract the container minutes later
([verified](https://docs.jmrp.io/mikroscope/about/status/#verified-remote-image-change)).

Until the tar is deleted, it and the root extracted from it share the disk, so `doctor` asks for
twice the image plus 4 MiB. With `--remote-image` nothing is uploaded: RouterOS pulls the layers,
there is no tar to wait for or delete, and `doctor` asks for the extracted root, 7 MiB, plus 4 MiB.
The root still goes where `--disk` and `--ephemeral` say.

## Install manifest

`install` writes the install manifest first, `mikroscope/<name>.manifest.txt` beside the container
root, and `uninstall` deletes it last. It records how the install was made and every object it
created:

```text
mikroscope-manifest=1
name=mikroscope
tag=mikroscope:mikroscope (managed by mikroscope)
disk=
veth=veth-mikroscope
subnet=172.30.10.0/30
port=9123
iface-list=LAN
addr-list=LANs
expose=
container-name=
remote-image=registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1
token=no
dir=mikroscope
file=mikroscope/mikroscope.manifest.txt
object=/interface/veth name=veth-mikroscope
object=/ip/address interface=veth-mikroscope
object=/interface/list/member interface=veth-mikroscope list=LAN
object=/ip/firewall/address-list list=LANs address=172.30.10.0/30
object=/container/envs list=mikroscope-env
object=/container interface=veth-mikroscope
dir=mikroscope/mikroscope
```

- The header lines are the install's shape, under the CLI's flag names. `status`, `upgrade` and
  `uninstall` read them, so they need no shape flag, and refuse one that contradicts them.
- `token=` says only whether a token was set, never its value. The manifest holds no secret.
- `dir=`, `file=` and `object=` list what the install created, in creation order: a path, or a
  menu and a selector that, with the tag, selects the object.
- Every install route writes it: the CLI's `install` and `upgrade`, and the `plan --rsc` script.
- An install without one, made by an older CLI or `--ephemeral` after a reboot, is read from its
  tagged objects instead.

## Envlist contents

The envlist holds the agent's configuration and the ownership marker, and nothing else:

The entries install writes into the agent's envlist:

| 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` |

No sink address, no sink token and no API credential goes into it. Treat everything in it, the token
included, as readable by any RouterOS user with `read`: such a user can list every container's
envlist over the API ([verified](https://docs.jmrp.io/mikroscope/about/status/#verified-read-user-envlist)). The plan prints the
token masked as `(token)`. [Security model](https://docs.jmrp.io/mikroscope/security/) says which credential lives
where, and [Environment variables](https://docs.jmrp.io/mikroscope/reference/environment/) lists the variables the agent
reads beyond these.

## Memory sizing

Move `--memory-max` and `--mem-limit-mb` with `--rate` and `--buffer`:

- The ring holds `rate × buffer` lines of about 3.5 kB each.
- The Go soft limit wants about twice the ring and has to sit comfortably under `memory-max`.
  `--mem-limit-mb 0`, the default, derives it: 16 MiB for 10 Hz and a 60 s ring under `64M`.
- The agent's startup check counts 3 456 B a line plus
  `--capture-mb`. Above `memory-max` it refuses to start; above half of `--mem-limit-mb` it warns.

The defaults held at 10, 50 and 100 Hz: `--buffer 60`, the derived `--mem-limit-mb` and
`--memory-max 64M` ([measured](https://docs.jmrp.io/mikroscope/about/status/#campaign-rates-2026-09-18)). What a tight limit costs is
on [Agent cost](https://docs.jmrp.io/mikroscope/cost/), and each rate on [Rate ceiling](https://docs.jmrp.io/mikroscope/cost/rate-ceiling/).

## See also

- [Install with the CLI](https://docs.jmrp.io/mikroscope/install/): the order these objects are created in.
- [Upgrade and uninstall](https://docs.jmrp.io/mikroscope/install/upgrade/#uninstall): how they are removed.
- [Rate ceiling](https://docs.jmrp.io/mikroscope/cost/rate-ceiling/): what each rate costs with the memory flags above.
- [Privileged mode](https://docs.jmrp.io/mikroscope/limits/privileged/): the one container setting that is a real
  privilege grant.
- [Security model](https://docs.jmrp.io/mikroscope/security/): what else can read the envlist.
