MikroDash
Open-source command center for RouterOS
Self-hosted and live over the MikroTik RouterOS v7 binary API: a single static binary, one Docker volume.
Quick start ·
One-click deploy ·
Features ·
Screenshots ·
RouterOS setup ·
Configuration ·
Security ·
Development
✨ Why MikroDash
- Live, not refreshed. MikroDash holds one connection per router and pushes changes to the browser over a WebSocket the moment they happen.
- Kind to small routers. Each RouterOS menu is read once however many pages want it, collectors sleep when nobody is looking, and every router can be switched between streaming and polling.
- One binary, one volume. A Go server with the TypeScript frontend built in, shipped as a multi-arch image. No database server, no agents on the router, no CDN: every asset is self-hosted, so it works on an isolated network.
- Safe by design. Credentials are encrypted at rest, every write is permission-checked and audited, and changes that could cut the dashboard off from the router are refused or warned about first.
🚀 Quick start
# docker-compose.yml
services:
mikrodash:
image: ghcr.io/secops-7/mikrodash:latest
restart: unless-stopped
ports:
- "3081:3081"
- "13231:13231/udp" # only for zero-touch provisioning; see below
volumes:
- mikrodash-data:/data
volumes:
mikrodash-data:
docker compose up -d
Open http://localhost:3081. On first run MikroDash asks you to create an administrator account, using the setup code it prints to its log (docker compose logs mikrodash), then walks you through adding your first router and testing the connection. No .env file is needed.
Images are published for linux/amd64, linux/arm64 and linux/arm/v7 on every release tag, so latest always means the latest release, never unreleased work. Pin a version with ghcr.io/secops-7/mikrodash:<version>.
Run it on the router itself (RouterOS container)
MikroDash can run on the MikroTik it monitors, using RouterOS’s own container support:
/container/add remote-image=ghcr.io/secops-7/mikrodash:latest \
interface=veth_mikrodash root-dir=usb1/mikrodash \
mountlists=mikrodash_data start-on-boot=yes comment="MikroDash"
Follow docs/routeros-container-install.md for the full walkthrough: enabling container mode, the veth and bridge, the input chain firewall rule (its absence is the usual cause of a bare “timed out” when adding the router) and the /data mount, without which every repull discards your database.
The RouterOS Apps menu installs MikroDash from MikroTik’s own copy of the catalogue, which they refresh on their own schedule and which can lag behind the current release. Adding it as an ordinary container, as above, always pulls the latest release.
Build from source
Only Docker is needed on the host:
git clone https://github.com/SecOps-7/MikroDash.git
cd MikroDash
docker build -t mikrodash:local .
docker run -d --name mikrodash --restart unless-stopped \
-p 3081:3081 -v mikrodash-data:/data mikrodash:local
The multi-stage build bundles the frontend with esbuild through its Go API (cmd/webbuild, so no Node runtime is involved), compiles a static Go binary with CGO_ENABLED=0, and copies both into an Alpine runtime with /data as its only mount. For a multi-arch build:
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t mikrodash:local --load .
A worked deployment on a separate Docker host is in docs/deploy-r5s.md.
🌐 One-click deploy

🧭 Features
At a glance
|
|
| 📊 Dashboard |
A drag-and-drop grid of cards (traffic, system health, network flow, network topology, connections, top talkers, WireGuard, and optional cards such as a world connections map, BGP peers, NetWatch, logs, a Security Score, and API diagnostics that show every router command by who asked for it). Layouts are saved to your account, so every browser shows the same arrangement. |
| 🛰️ Multi-router fleet |
Manage many routers from one install. Switch from the header, where devices are grouped by site, with no reload. The Devices page shows the whole fleet as cards, a sortable list or a world map, filterable by site: each device’s 24-hour connectivity, last backup, open alerts and available updates at a glance. Click one for its own overview - connectivity history and outages, live usage, ports, clients, its public IP and recent alerts - without leaving the device you are on. Routers are placed on the map by their public IP until you pick a town, and a whole list of devices can be added at once from a CSV (Settings -> Devices -> Import). |
| 🔐 Users and access control |
Per-user accounts with editable roles (a read and write matrix per page), granted to users or groups over everything, a site, or a single router. Or sign in with an existing account: single sign-on against any OpenID Connect provider (Entra ID, Okta, Keycloak, Authentik), with the role decided by a claim your provider sends. The password form never goes away, so the local administrator is always a way back in. |
| 🔔 Alerts and notifications |
Interface up/down, WireGuard peers, CPU, ping loss, NetWatch hosts, router online/offline, RouterOS updates, configuration drift and backup failures, delivered through notification channels you create: SMTP, or a webhook URL for Telegram, Pushbullet, ntfy, Discord, Slack, Gotify, Pushover, a generic JSON endpoint, or an Apprise server for everything else. Each channel picks its own alert types, devices, thresholds and cooldown, and scheduled reports go out through one. |
| 📈 History and reports |
Traffic, ping, bandwidth, alerts and connectivity recorded to SQLite, viewable by date range, exported to CSV or PDF, and emailed on a daily, weekly or monthly schedule. Click any interface for its own traffic: live for the last minute, or up to thirty days back for the interfaces you choose to record. |
| 🪄 Zero-touch provisioning |
Add a router before it exists: a wizard makes a short script for it, and when the router runs it, it calls home, joins the fleet and receives its Config Management template on its own. Remote routers dial in over a WireGuard tunnel built into MikroDash, from behind NAT anywhere; a generic script brings in a whole rollout, each router waiting for your approval. |
| 🧩 Config Management |
A library of 21 ready-made configuration templates (firewalls, a threat IP blocklist, VLANs, a guest network, WireGuard, DNS, queues, monitoring) plus your own, captured from a router or written in the editor. Every setting has a working default. Deploy to one router or the fleet: a syntax check first, a restore point, a canary router before the rest, an automatic revert if a change cuts MikroDash off, and History and Drift afterwards. |
| 💾 Backups |
Scheduled configuration backups kept only when something changed, with a unified diff of what moved, retention rules, and a guarded restore. Optionally a migration export as well, per device: the full configuration with its passwords and keys included, encrypted at rest, for rebuilding on a different device. |
| 🛠️ Tools |
Ping, traceroute with an animated world map of the hops, torch, bandwidth test and a packet sniffer with a pcap export, run from the router with live output and a Stop button. |
| ⌨️ Terminal |
A RouterOS console in the browser, for the device you have selected. The line you type goes to the router’s own parser, so where, :put [...] and everything else the CLI understands works, and what comes back is the router’s own text. Granted per device through the Terminal page permission, and bounded by what the RouterOS account MikroDash signs in with may do. Every line is in the audit trail. |
| 🛡️ Security Scan |
Audits the router’s configuration (management access, firewall, exposed services, accounts, system, wireless and certificates), scores it, and links each finding to the page where it is fixed. The score is also a Dashboard card, and the AI Agent can run the scan when you ask how secure a router is. |
| 🤖 AI Agent |
An optional assistant that answers questions from live router data and makes changes through the same checks, audit trail and undo as the forms: one row, or several as a plan you approve once. It can reboot, upgrade, sign a certificate, set the clock, run a script, read a file or an export, and undo its own change, always with your confirmation. Works with any OpenAI-compatible endpoint, including local models. |
| 📦 Containers |
RouterOS containers with their env lists, mounts and interfaces, plus an Apps tab that browses RouterOS’s app store and installs an app in one click. |
| 🧾 Audit trail |
Every write, allowed or refused, with who, where, what changed and the outcome. Filterable and exportable to CSV. Credential values are never stored. |
| 🧪 Optional features |
Extras that stay off until you switch them on in Settings -> Features: DNS bulk edit and export, a DNS fleet sync to a reference router, domain list subscriptions kept as FWD entries, a DNS cache browser and domain route trace, a Domain Routing card and alert, a device watch list that alerts when a device goes missing, Wi-Fi client history, Wi-Fi map weak spots and CAP version checks. Switched off, a feature reads nothing from your routers. |
| 🎨 Make it yours |
26 colour palettes, 26 self-hosted fonts, contrast and brightness controls, visible-page presets, a grouped sidebar, and custom branding (name and icon). |
Pages
MikroDash has more than fifty pages. Here they are by area (the sidebar can group them too, or you can hide the ones you do not use). Most read live data; with write access, the configuration pages edit the router directly.
| Area |
Pages |
| Overview |
Dashboard, Devices, Config Management, Network Topology, WAN |
| Wireless |
Wifi Networks, Wifi Clients, Wifi Map (draw your site over a floor plan or map and see clients around each access point), CAPsMAN (both the wifi and legacy stacks) |
| Network |
Interfaces, IP Addresses, VLANs, Bridges, DHCP, DHCP Servers, DHCP Clients, DNS, IP Pools, Interface Lists, ARP |
| Routing |
Routing (routes and BGP), Routing Tables, Routing Rules, OSPF, VRRP |
| Tunnels |
VPN (an overview of every tunnel technology, with a card per live connection), WireGuard (interfaces and peers, with a client configuration and a scannable QR code), PPP, IPsec, OpenVPN, PPPoE Clients |
| Traffic |
Connections (a world map, or a list of every connection), Bandwidth, Queues, Logs |
| Security |
Firewall (Filter, NAT, Mangle and Raw, with reorder, undo and redo), Address Lists, Certificates, Security Scan |
| System |
Users (RouterOS accounts and groups), Services, Packages, Scripts, Scheduler, NTP Client, Clock, Logging, SNMP, Files, Containers, NetWatch |
| MikroDash |
Tools, Terminal, AI Agent, Reports, Backups, Audit Trail, Settings |
Every table sorts by its headers, except the ones where order is meaning (firewall rules, queues, routing rules, IPsec policies): those always show the router’s order, with move arrows.
How writes are kept safe
- Lockout guards. Changing or removing the address, interface, service, route, firewall rule, certificate or account MikroDash itself connects through is refused or raises a warning first, because nothing in the app could undo it.
- Secrets are never read back. WiFi passphrases, PPP and OpenVPN passwords, IPsec pre-shared keys and SNMP passwords are not requested from the router at all, so no page can display one. A blank password field means “keep the current one”.
- One deliberate exception, and it is audited. A WireGuard peer’s client configuration contains that peer’s private key, which is the point of it. It is fetched by a one-shot request rather than carried on the socket, needs write access to the WireGuard page on that router, is refused outright when sign-in is off or no audit database exists, and writes a row naming who revealed it before the router is asked. The key itself never enters that row.
- Code is for global administrators. What a script, scheduler task, VRRP script or container image runs is RouterOS code, so changing it needs a global administrator.
- Reboots ask for the router’s name. Rebooting, applying package changes, upgrading RouterOS or restoring a backup requires the router’s name typed back.
- Files that would act are refused. A file created or downloaded to the router may not be named to run on arrival (
*.auto.*) or to install at the next reboot (.npk). A file’s contents are read only when you ask, capped, with credential values hidden.
- Config Management deploys carefully. A syntax check before anything runs, a restore point per router, a canary router whose name you type before the rest, an automatic revert if a change cuts MikroDash off, and a fresh login to prove it did not.
- No sign-in, no writes. With sign-in turned off, every configuration page is read-only.
The AI Agent in detail
Off by default. Nothing is sent anywhere until you switch it on and configure an endpoint.
- Any OpenAI-compatible endpoint: a hosted provider, a gateway, or a model on your own hardware (Ollama, LM Studio, vLLM, LiteLLM). Configure it under Settings, AI Agent, and use Test Connection; an endpoint with a self-signed certificate is trusted by pinning it from there, never by turning checking off. A hosted endpoint receives router names, addresses and network shape; a local one sends nothing outside your network.
- Reads what you can read. It answers from the router’s current data and can look up any table your role permits, never a page your role denies.
- Changes go through the forms’ own path: one row at a time, or several as a plan you approve once, with the same permission checks, lockout guards, undo history and audit entry. It can undo its own most recent change, and run the pages’ own actions (reboot, upgrade, sign a certificate, run a script, download a file), each confirmed.
- Only the router you selected. It reads the others’ status from MikroDash’s records and cannot reach them. It reads files and configuration exports with credentials hidden, and a WireGuard client configuration opens in your browser, never in the conversation.
- Raw RouterOS commands are off unless a global administrator switches them on (Settings, AI Agent). Even then they are offered only to global administrators, and every command, reads included, runs only when you type the router’s name.
- Asks before changing anything by default. Deletes, and changes that could cut MikroDash off from the router, always ask.
- Keeps the conversation per user and per router, so follow-up questions work. Clear deletes it, and it expires after the retention period you set.
- Agent Overview card: an optional dashboard card with a one-line router status written by the assistant.
docs/mikromcp-parity.md maps what the assistant can reach.
Zero-touch provisioning in detail
Off by default. Switch it on under Settings, Provisioning, and give the public name or address routers will dial.
- Add device on the Devices page asks where the router will be. Remote routers dial this MikroDash over WireGuard, so they can be anywhere with internet access; Local routers call home over your LAN and need no tunnel. Name it, optionally give its serial number (only that router can then use the script), pick its sites and a template, and download the script.
- Run the script on the router: upload it and run
/import, or paste it into a terminal. It sets up the tunnel and an API user limited to MikroDash’s address, then calls home every minute until MikroDash answers, and removes itself.
- On arrival the router is added to the fleet and its template is previewed on it and deployed through Config Management, with a restore point and the auto-revert, as the person who added it.
- A generic script (Settings, Provisioning) serves a whole rollout. Each router that runs it waits on the Devices page as Not yet provisioned, with its serial, model and RouterOS version, until you Onboard it (name, sites, template) or Reject it. Nothing connects to it before then.
- Only your instance. Every script carries this MikroDash’s WireGuard key and instance ID. Scripts are shown once and expire; making a new one stops the old one working. The router’s API password is made on the router (remote) and sent only inside the tunnel.
- One UDP port, 13231 by default, must be published and forwarded to MikroDash. The WireGuard server runs in userspace, so the container needs no extra privileges.
Keyboard shortcuts
| Key |
Opens |
1 |
Dashboard |
2 |
WAN |
3 |
Wifi Networks |
4 |
Wifi Clients |
5 |
CAPsMAN |
6 |
Interfaces |
7 |
DHCP |
8 |
DNS |
9 |
VLANs |
/ |
Logs, with the search box focused |
📸 Screenshots
 Connections map |
 Connections |
 Wifi clients |
 Interfaces |
 Firewall |
 Bandwidth |
 DHCP |
 VPN and WireGuard |
 Routing |
 Logs |
🔌 RouterOS setup
Create a dedicated, read-only API user. Every page, chart and alert works with it, and a compromised dashboard cannot change your router:
/ip service set api port=8728 disabled=no
/user group add name=mikrodash policy=read,api,test,!local,!telnet,!ssh,!ftp,!reboot,!write,!policy,!winbox,!web,!sniff,!sensitive,!romon,!rest-api
/user add name=mikrodash group=mikrodash password=<a-strong-password>
Optional: enable the write features
Pages that change the router need more than read:
| Page |
Needs |
| Configuration pages (Firewall, Routing, DNS, DHCP, VLANs, Bridges, Interfaces, VPN, Queues, Packages and the rest) |
write |
| Users (RouterOS accounts and groups), and credential profiles |
write and policy |
| Backups |
write and ftp |
| Terminal |
whatever the line you type needs - see below |
ftp governs reading and writing files on the router, which /export file= and /system/backup/save need; it does not enable the FTP service. policy governs user management, so an account holding it can create router users. Grant it deliberately, not by default.
/user group set [find name=mikrodash] policy=read,write,policy,api,test,!local,!telnet,!ssh,!ftp,!reboot,!winbox,!web,!sniff,!sensitive,!romon,!rest-api
Without these nothing breaks: a page that is refused drops to read-only and shows the command it needs. MikroDash never lets you edit the account it signs in with, or that account’s group.
Credential profiles need the same two policies, for the same reason: a profile (Config Management -> Credentials) creates a RouterOS account on every device you link it to, which is user management. Each profile creates its own group with the policies you choose, so it never shares a group with MikroDash’s own account, and that account can never be taken by a profile - the same guard refuses it per device. See SECURITY.md on what a fleet-wide password is worth.
The MikroDash login is the one exception, and it is deliberate. The pinned “MikroDash login” row on the same tab keeps the password of the mikrodash account MikroDash itself signs in with, in one place. Linking a device creates that account in a mikrodash group, or adopts the one you made by hand, and MikroDash then signs in through the profile. Changing the password changes it on every linked device or on none: each device must be online, each is confirmed with a fresh sign-in, and any failure puts the old password back everywhere.
The Terminal page is the exception to that whole table, because it does not know what your line will do. It sends what you type to the router’s own console and shows the reply, so the policies above are not a list it consults - they are the limit the router enforces on it. On the read-only account at the top of this section it can read and nothing else, because RouterOS refuses the rest.
Who may use it is a permission, per device: grant a role write access to the Terminal page in Settings -> Authentication -> Access Management. Administrators have it already; nobody else does until you say so, and a grant on one device does not carry to another. Every line is recorded in the Audit Trail whether it worked or not. There is no guard, no read-back and no undo on this page, which is the point of it and the reason the permission is worth granting deliberately.
If a queue seems to do nothing, check FastTrack. FastTracked connections bypass simple queues, and the Queues page tells you when that is happening.
Optional: encrypt the API connection (API-SSL)
A self-signed certificate is enough:
/certificate add name=local-ca common-name=local-ca days-valid=3650 key-size=2048 key-usage=key-cert-sign,crl-sign
/certificate sign local-ca
/certificate add name=api-ssl-cert common-name=mikrodash days-valid=3650 key-size=2048 key-usage=digital-signature,key-encipherment,tls-server
/certificate sign api-ssl-cert ca=local-ca
/ip/service set api-ssl certificate=api-ssl-cert disabled=no port=8729
Then edit the router in MikroDash, enable TLS and Allow self-signed cert, and set the port to 8729.
Or let MikroDash do all of the above: Auto Switch to API-SSL in the device’s edit dialog creates the certificates, enables the service, and moves the connection across once the device answers on 8729. The plain API is left on.
Optional: fill the Top Talkers card (Kid Control)
The Dashboard’s Top Talkers card reads per-device rates from RouterOS Kid Control. RouterOS only tracks devices there once at least one Kid Control profile exists, so on a router without one the card says “No devices”.
Create one profile that allows everything:
/ip kid-control add name=monitor mon=0s-1d tue=0s-1d wed=0s-1d thu=0s-1d fri=0s-1d sat=0s-1d sun=0s-1d
Within a few seconds RouterOS starts listing the devices it sees on your LAN. Kid Control only restricts devices assigned to a profile, and none are, so this blocks nothing. The all-day schedule is there in case a device is ever assigned to it: without it, RouterOS defaults to 7h-21h.
Devices found this way have no name. To name them from your DHCP leases (the lease comment if it has one, otherwise the client’s hostname), wait about ten seconds and paste:
:foreach l in=[/ip dhcp-server lease find] do={
:local mac [/ip dhcp-server lease get $l mac-address]
:local nm [/ip dhcp-server lease get $l comment]
:if ($nm = "") do={ :set nm [/ip dhcp-server lease get $l host-name] }
:local d [/ip kid-control device find where mac-address=$mac]
:if ($nm != "" && [:len $d] > 0) do={
:if ([/ip kid-control device get $d name] = "") do={
:do { /ip kid-control device set $d name=$nm } on-error={
/ip kid-control device set $d name=($nm . " (" . [:pick $mac 9 17] . ")")
}
}
}
}
- Naming a device does not assign it to the profile, so it stays unrestricted.
- Names must be unique, so a duplicate gets the end of its MAC added, e.g.
Living Room TV (E2:57:BE).
- Only devices that are online now get named. The script only names devices that have no name yet, so it is safe to run again, or on a schedule: save it as a script called
kc-names, then /system scheduler add name=kc-names interval=1h on-event=kc-names.
🔧 Configuration
Almost everything is configured in the web UI (Settings) and stored on the /data volume: routers, users and roles, notification channels, poll intervals, retention and appearance. /data is the only mount, and it holds encrypted settings, the user store, the SQLite history database and configuration backups.
Environment variables
All optional.
| Variable |
Purpose |
MIKRODASH_ORIGINS |
Hostnames the browser uses to reach MikroDash, comma separated (wildcards like *.example.com work). Required behind a reverse proxy; see below. |
MIKRODASH_TRUSTED_PROXIES |
IPs or CIDR ranges of your reverse proxies. Their X-Forwarded-For is believed, so the login limit and audit trail see real visitors. Empty trusts none. |
MIKRODASH_ROUTER_ADDRESSES |
Which router addresses MikroDash may connect to: private (the default: your LAN, VPNs, Tailscale and the internet, never this machine’s loopback or a link-local address such as a cloud metadata service), public (the internet only), or any (also loopback, for a router reached through a tunnel on this machine). MikroDash refuses to start on any other value. |
FORCE_HTTPS |
true marks the session cookie Secure, for use behind a TLS-terminating proxy. Plain http:// sign-in stops working while it is set. |
DATA_SECRET |
The key for credentials at rest. Overrides the key generated on first run and saved to /data/.secret. |
LOG_HISTORY_SIZE |
How many router log lines to keep in memory for the Logs page. |
MIKRODASH_ROUTER_CONCURRENCY |
The cap on API commands in flight per router. |
TZ |
The timezone of the container’s log lines. Schedules follow the display timezone set in Settings instead. |
Command-line flags
The image passes these as its CMD; give the container its own command: to override them.
| Flag |
Image default |
Purpose |
-listen |
:3081 |
Address to serve on |
-data |
/data |
Data directory |
-history |
on |
Record traffic, ping and connectivity history for Reports |
-backup-scheduler |
on |
Take scheduled configuration backups |
-retention |
on |
Run the daily sweep that ages old data out of the database |
-alert-dispatch |
on |
Send alert notifications and scheduled report emails |
-no-pool |
off |
Do not hold background connections to routers nobody is watching |
-origins |
empty |
Same as MIKRODASH_ORIGINS |
-trusted-proxies |
empty |
Same as MIKRODASH_TRUSTED_PROXIES |
-router-addresses |
private |
Same as MIKRODASH_ROUTER_ADDRESSES |
-geo |
/app/geo |
Directory with the bundled geo-IP database and city list |
The four feature switches are off in the bare binary and on in the image, because each is unsafe to run twice against the same routers: two instances would take every backup twice and send every alert twice. If you run a second instance against the same fleet, turn them off there.
GET /healthz reports startup and router connection state, and is what the container health check uses.
🔒 Security
MikroDash is built for your local network. It serves plain HTTP; terminate TLS at a reverse proxy if you need it.
- Turn on sign-in. Settings, Authentication, Require sign-in. With it off, anyone who reaches the page can see all of your routers’ data (router configuration stays read-only). With it on, access is granted as a role (which pages, read or write) over a scope (everything, a site, or one router).
- Where a device is reached is an administrator’s. Write access to the Devices page, granted over one router, lets someone rename that device and tune its alerts, but not change its address, port, TLS settings or the account MikroDash signs in with: MikroDash sends the stored password to wherever the device points. And MikroDash never connects to a router on its own machine’s loopback or a link-local address (where cloud metadata services answer) unless
MIKRODASH_ROUTER_ADDRESSES=any says so.
- Zero-touch provisioning opens one UDP port (13231, WireGuard) while it is switched on. It is the only part of MikroDash meant to face the internet: a router without a valid script cannot complete a handshake with it.
- First-run setup needs a code from the log. Until an account exists, MikroDash prints a one-time setup code to its log, and the setup page will not create the administrator without it, so a stranger who reaches a fresh install cannot claim it.
- Never expose it directly to the internet. For remote access use a VPN, or an authenticating proxy such as Cloudflare Access or Authelia in front of MikroDash with sign-in enabled.
- Credentials at rest are encrypted with AES-256-GCM; user passwords are hashed with scrypt. Keep the
/data volume, and /data/.secret in particular, private.
- Single sign-on is OpenID Connect, with PKCE, a nonce, and the ID token checked against the provider’s own key set. An account whose username collides with a local one is refused rather than merged, and a person whose claims map to no role cannot sign in at all.
- The backup migration export is off by default, and stores that device’s passwords and keys on the server when you turn it on. Encrypted at rest, which protects a copy of the volume - not somebody who already has
/data.
- Sign-in is rate limited per client, and every change is recorded in the audit trail.
To report a vulnerability, see SECURITY.md.
Behind a reverse proxy
MikroDash refuses a WebSocket whose Origin does not match the Host it sees, which is what stops a hostile page opening an authenticated socket to a MikroDash you are signed in to. Behind a proxy the two differ, so the UI loads and then stays empty, with this in the log:
[ws] accept: failed to accept WebSocket connection: request Origin "dash.example.com" is not authorized for Host "..."
Name the host the browser uses, including the port when it is not the default:
environment:
- MIKRODASH_ORIGINS=dash.example.com
- MIKRODASH_TRUSTED_PROXIES=172.18.0.0/16 # your proxy's address or network
Alternatively, have the proxy preserve the original Host header (proxy_set_header Host $host; in Nginx). X-Forwarded-Host is deliberately not trusted, since any client can send one.
Remote access through a Cloudflare Tunnel
MikroDash does not ship cloudflared, but it runs well beside Cloudflare’s own container. This puts the login page on the internet, so first make sure sign-in is required, put Cloudflare Access in front of the hostname, and use strong passwords.
- In the Cloudflare dashboard (Zero Trust, Networks, Tunnels), create a tunnel and copy its token. Add one public hostname, for example
dash.example.com, pointing at http://mikrodash:3081.
- Add the sidecar and put the token in
.env as TUNNEL_TOKEN=...:
services:
mikrodash:
# ... your existing service ...
environment:
- MIKRODASH_ORIGINS=dash.example.com
- MIKRODASH_TRUSTED_PROXIES=172.18.0.0/16 # the network the two containers share
- FORCE_HTTPS=true
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel --no-autoupdate run
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
restart: unless-stopped
depends_on:
- mikrodash
- Find the shared network’s range with
docker network inspect <project>_default --format '{{(index .IPAM.Config 0).Subnet}}'. MikroDash refuses to start if MIKRODASH_TRUSTED_PROXIES does not parse.
FORCE_HTTPS=true means plain http:// sign-in on the LAN stops working; use the tunnel hostname everywhere, or leave it off. The official cloudflared image covers linux/amd64 and linux/arm64 only.
🧩 How it works
RouterOS binary API (TCP / TLS)
│
internal/routeros adapter over go-routeros (a patched copy in third_party/)
internal/roscache one read per menu, shared by every page that wants it
internal/collect collectors, one per RouterOS subsystem
internal/session one session per router, owning its shared connection
internal/alert alert rules internal/guard write guards
internal/store /data: encrypted settings, users, routers
internal/db SQLite history and audit (pure Go, so the binary is static)
internal/server HTTP routes and the WebSocket protocol
│
web/src the TypeScript frontend
Concurrent API channels, not data volume, are what strain a small router, so the collector layer is built to ask for less: live data streams, configuration is polled, and a collector runs only while a page, card, alert or report needs it. Collector-Architecture.md describes it in full.
Geo-IP data comes from DB-IP - City Lite for country and city, ASN Lite for the organisation that owns an address - both bundled into the image at build time under CC BY 4.0. Lookups happen locally; nothing leaves the machine.
💻 Development
Go runs in a container, so Docker is the only hard requirement. Node is needed only to type-check and test the frontend.
(cd web && npm ci) # once: TypeScript and esbuild for the frontend checks
sh tools/verify.sh # everything: gofmt, vet, go test, generated code, tsc, web tests
sh tools/verify.sh --no-docker # frontend half only
tools/verify.sh discovers what to check rather than working from a list, so a new Go test or *.test.ts file runs without being registered anywhere. You do not need a MikroTik to contribute: MikroDash starts without one and shows the setup wizard.
Read CONTRIBUTING.md for the local setup and the project’s conventions, and Collector-Architecture.md before changing how data is read from a router.
🤝 Contributing
Issues and pull requests are welcome, from typo fixes to new pages. Check the open issues first, and open one before a large change so the approach can be agreed. See CONTRIBUTING.md.
📄 License
MIT, see LICENSE. Third-party attributions are in THIRD_PARTY_NOTICES.md.
MikroDash is an independent community project and is not affiliated with, endorsed by, or associated with MikroTik SIA. MikroTik and RouterOS are trademarks of MikroTik SIA.
Built with the help of Claude by Anthropic.
评论