Skip to content
Projects

mrtian2016/routeros-lanpulse

View on GitHub

mrtian2016/routeros-lanpulse has 43 GitHub stars and 14 forks. GitHot measured 0 stars in 24 hours, +1 in 7 days, and +1 in 15 days. Last checked 2026-11-09 22:04 UTC. Last push 2026-08-25.

How these counts are measured

MikroTik RouterOS 的实时流量面板 · A live dashboard for MikroTik RouterOS — flow diagram, event stream, 1-second resolution

Measured star growth

in 24h
0
in 7 days
+1
in 15 days
+1
in 1 month
Collecting history

Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.

README.md

Original

routeros-lanpulse

A live dashboard for MikroTik RouterOS

One page that answers: who is using the bandwidth, where traffic enters and leaves, whether the tunnels are up, and how hot the machines are running.

RouterOS · Prometheus · no build step · zh / en · light / dark

中文 · RouterOS setup · Configuration

→ Open the live demo

The real page, running on a redacted snapshot of a real network. Both themes, both languages, every panel — nothing to install. Every device name, IP and site name you see there is a pseudonym produced by the built-in redaction mode (see Redaction).


Before you clone: this is a RouterOS dashboard

A MikroTik router is not one data source among many — it is the foundation. Eight of the fifteen panels come from it, including everything that makes the page worth looking at:

Needs RouterOS Optional add-ons
WAN throughput + 24 h history Proxmox VE guests / storage
The flow diagram itself Chassis temperature & fans (IPMI)
Per-device LAN traffic (via kid-control) NAS capacity & disk health (SNMP)
WireGuard peers Wi-Fi client signal & rates (UniFi)
OpenVPN dial-in users Per-device destinations (NetFlow)
Device names (from DHCP leases) Tunnel / ingress accounting (textfile)
Half the event stream (router log)
Public IP

Two things in particular do not port to other platforms: per-device traffic relies on RouterOS kid-control, which has no equivalent on OPNsense or OpenWrt, and the 1-second resolution comes from a persistent connection to the RouterOS binary API.

If you do not run MikroTik, this project will show you an empty page. That is not a bug — it is what it is built on.

Why

Grafana is powerful, but answering "what is my network doing right now" means scanning a dozen panels. lanpulse puts that on one page: a live flow diagram, an event stream, and a few tables that matter.

  • 1-second resolution — WAN, per-device and tunnel rates are computed by reading the router's counters directly, so they are not limited by the Prometheus scrape interval.
  • Events, not just graphs — devices joining and leaving, tunnels dropping, traffic bursts, disks filling up, fans stopping. Changes are what you actually want to see.
  • Only shows what you have — no IPMI, no hardware panels; no UniFi, no wireless section. Turn modules off in the config and they disappear cleanly.
  • No build step — the frontend is plain ES modules, the backend is Python standard library only. docker compose up and you are done.

Quick start

Do the router-side setup first — a read-only user, the API service and a kid-control profile. It takes five minutes and skipping it is the number one reason the dashboard comes up empty.

git clone https://github.com/mrtian2016/routeros-lanpulse.git && cd routeros-lanpulse
cp config.example.toml config.toml     # your addresses and names
cp .env.example .env                   # your passwords
docker compose --profile router up -d

Updating later: git pull && docker compose pull && docker compose up -d. If you changed the code yourself, add --build.

Open http://<your-host>:9132.

Profiles select which exporters run — start only what you actually have:

profile collects
(none) dashboard + Prometheus only
router RouterOS (via mktxp)
pve Proxmox VE
ipmi chassis temperatures / fans
snmp Synology NAS, switches
netflow per-device destination sankey
unifi UniFi wireless client signal / rates
all everything

The demo, and hosting your own

The live demo is nothing but docs/demo/: plain HTML built from a redacted snapshot, with no backend and no Prometheus behind it. Drop it on GitHub Pages or any static host, or run it locally.

python3 -m http.server -d docs/demo 8000

It is generated by scripts/make-demo.py from a running instance that has redaction enabled; the generator refuses to run against an instance that is not redacted, so real data never ends up in the demo.

Settings page

The dashboard itself needs no login on your LAN. Changing the configuration does.

Set ADMIN_PASSWORD in .env, then the ⚙ button opens a settings page where you can edit config.toml directly: the TOML is validated before anything is written, the previous version is kept as config.toml.bak, and changes take effect immediately — only start-up settings such as the listen port need a container restart.

Without ADMIN_PASSWORD the settings page is closed entirely. That is deliberate: the most common way a self-hosted project gets owned is a default password left in place, so there is no default password to leave.

Sessions are HttpOnly + SameSite=Strict cookies with an 8-hour lifetime; five wrong passwords from one IP locks it out for five minutes.

Requirements

Required: a MikroTik router running RouterOS, mktxp to scrape it, and Prometheus (included in the compose file). RouterOS 7.x is what this is developed against; 6.x exposes most of the same metrics but is untested.

Optional — turn off what you do not have and the matching panels hide themselves: Proxmox VE (prometheus-pve-exporter), IPMI/BMC (ipmi-exporter), Synology (snmp-exporter), UniFi (bundled exporter, API-key based, no local account needed), NetFlow v9 (bundled collector), and tunnel/ingress accounting via the textfile scripts in exporters/textfile/.

Configuration

Everything lives in one config.toml that holds identities only — addresses, names, toggles — never passwords. Secrets go in .env, so you can paste your config into an issue without redacting it first.

[router]
host = "192.168.1.1"
wan_interface = "pppoe-out1"   # pppoe-outN when dialling, the physical port otherwise
lan_interface = "bridge"

[[hardware]]                   # one block per machine with a BMC; delete them all if none
title = "Main host"
bmc = "host-a"
instance = "192.168.1.10:9100"
storage = "pve"                # pve = storage pools | fs = filesystems | none

[topology.nodes]               # the shape of the diagram; delete a node and its links go too
internet = { x = 80,  y = 250, w = 110, h = 48, name = "Internet", sub = "Public IPv4" }
ros      = { x = 330, y = 250, w = 140, h = 56, name = "RouterOS", core = true }

See config.example.toml — every option is commented.

Redaction

For posting screenshots publicly, or letting someone see the dashboard without exposing your network:

[redact]
enabled = true

Public IPs, private IPs, MAC addresses, domains, device names and site names are replaced with deterministic pseudonyms: the same device always maps to the same fake name, so the relationships in the diagram stay intact, but the mapping is not reversible. Public addresses land in 203.0.113.0/24 (RFC 5737 documentation range).

scripts/leakscan.py audits the result — it searches the redacted output for real identifiers and reports anything that slipped through.

Alerting

Events can be pushed to Bark (iOS) and Telegram:

[alerts]
enabled = true
lang = "en"                  # notification language, independent of the UI language
levels = ["warn", "bad"]
cooldown_sec = 300
startup_quiet_sec = 60
max_per_hour = 20
min_burst_mbps = 200         # separate push threshold for burst events, 0 = off

[alerts.bark]
enabled = true
server = "https://api.day.app"

[alerts.telegram]
enabled = true
api_base = "https://api.telegram.org"

Credentials go in .env: BARK_KEY, TELEGRAM_TOKEN, TELEGRAM_CHAT_ID. The settings page has a "send a test notification" button that reports per-channel results.

Per-event-type switches

Eleven event types each have their own switch. Types that are off still appear on the dashboard — they just do not reach your phone:

[alerts.kinds]
hw = true          # hardware: temperature / fans / disk full / RAID
branch = true      # branch tunnels going down and coming back
wan = true         # WAN link up/down
vm = true          # guests starting and stopping
ovpn = true        # OpenVPN dial-in users
wg = false         # WireGuard peers (phones roam between networks, this gets chatty)
ts = false         # tailnet nodes
lan = false        # LAN devices joining / leaving
wifi = false       # Wi-Fi join / leave / roam
ingress = false    # ingress connection counts
burst = false      # traffic bursts

Defaults follow one rule: does this need you to do something? Actionable types default to on, informational ones to off. The settings page has the same switches, and writing from there preserves your comments — the standard library has no TOML writer, and re-serialising the whole file would drop every comment, so the write is line-based.

Types absent from the config default to on. When an upgrade adds a new event type, being briefly noisy beats silently swallowing it.

Dashboard thresholds and push thresholds are different things. The floors in [events] are set low on purpose — a burst shows up on the panel at 8 Mbps so you can see trends at a glance. But a normal download should not buzz your phone, so the alert side has two separate knobs:

  • [alerts] min_burst_mbps — only bursts above this are pushed (the panel still shows all)
  • [events] burst_level = "info" — demote bursts so they appear on the panel but never notify

threshold_level does the same for temperature and disk events. Events carry their magnitude in a v field, which is what the size filter reads.

Deliberate choices:

  • Startup quiet period — on boot the router's log buffer gets replayed; without a quiet window every restart would fire a screenful of notifications.
  • Deduplication and an hourly cap — a flapping tunnel is the fastest way to make alerts useless. Better to miss one than to be noisy; noisy alerts are ignored alerts.
  • Sent from a background thread — pushes go over the internet; a slow or hanging request must not stall the once-per-second event engine. One channel failing never affects the other.
  • Telegram's API is unreachable from some regions — point api_base at your own reverse proxy, or set https_proxy on the container (urllib honours it by default).

Architecture

browser ──1s──> lanpulse-agent ──┬──> Prometheus ──> exporters
                                └──> RouterOS API (8728, persistent connection)

The agent aggregates one state.json from Prometheus, and separately keeps a persistent RouterOS binary-API connection to read counters every second for the fast lane. (Binary API rather than REST: REST writes a login/logout pair to the router log on every request, and polling once a second overflows the log buffer within minutes.)

The frontend is plain ES modules with no bundler. Configuration is injected into the page by the backend, so startup does not wait on any fetch.

Contributing

Translations are especially welcome — lanpulse/web/js/i18n.js is a plain lookup table that uses the Chinese source string as the key, so a missing entry falls back to readable text instead of a panel.lan.title placeholder.

License

MIT

Explore related projects