Notification + Monitoring Stack¶
Notification gateway, network monitoring, and traffic analysis pipeline.
Architecture¶
graph TD
SVC[Services] -->|alerts| APP
APP["Apprise :8008"] --> TG["Telegram (Homelab group)"]
MK["MikroTik RB5009"] --> MKTOOLS["mikrotik-tools :8087
9 scheduled jobs"]
MK -->|IPFIX :12055| NF2NG["netflow2ng"]
MK -->|Exporter metrics| MKTOOLS
MK -->|REST API :443/rest| MKTOOLS
MKTOOLS --> EXP["Exporter → VictoriaMetrics :8428"]
MKTOOLS --> VMALERT["vmalert → Apprise on alert"]
MKTOOLS --> NTFYBR["ntfy-bridge → Apprise enriched alerts"]
NF2NG --> NTOPNG["ntopng :3004
(DPI + flows, bytes pending fix)"]
Components¶
Notification¶
| Component | Server | Port | Purpose |
|---|---|---|---|
| Apprise | development | 8008 | Notification gateway → Telegram only |
| ntfy | development | 9282 | Push notification backend (phone app) |
Apprise Config Keys¶
| Key | ntfy topic | Used by |
|---|---|---|
/notify/all |
media | Generic services |
/notify/infra |
komodo | Infrastructure events (ASN update, port-forward, DHCP) |
/notify/ros-alerts |
ros-alerts | vmalert → ntfy-bridge enriched alerts |
Telegram only — ntfy removed from all apprise config keys.
Sending Notifications¶
# Stateful (preferred)
curl -X POST http://100.126.172.96:8008/notify/all \
-H "Content-Type: application/json" \
-d '{"title": "Alert", "body": "message"}'
# Stateless (backward compatible)
curl -X POST http://100.126.172.96:8008/notify \
-H "Content-Type: application/json" \
-d '{"urls": "ntfy://...", "title": "...", "body": "..."}'
Network Monitoring¶
| Component | Server | Port | Purpose |
|---|---|---|---|
| mikrotik-tools | development | 8087 | 9 scheduled jobs (supervisord) |
| Exporter | development | — | ros_* metrics → VictoriaMetrics |
| Web Monitor | development | 8099 | MikroTik dashboard |
| ntfy-bridge | unraid | 9093 | vmalert webhook → Apprise (enriched) |
| vmalert | unraid | 8880 | Alert rules → ntfy-bridge |
| VictoriaMetrics | unraid | 8428 | Metrics storage |
Traffic Analysis¶
| Component | Server | Port | Purpose |
|---|---|---|---|
| ntopng | development | 3004 | Web UI + DPI (nDPI 468 protocols) |
| netflow2ng | development | 12055 | NetFlow v9 collector v0.2.2 → ZMQ → ntopng |
| GoFlow2 | development | 12056 | NetFlow → Kafka 'network-flows' (raw flow data) |
| AdGuard | unraid | 3003 | DNS filtering + query log (24h retention) |
Two pipelines (parallel):
MikroTik RB5009
├─ :12055 → netflow2ng v0.2.2 → ntopng (:3004) ← UI + DPI
│ └─ custom_flow_export.lua (embedded in entrypoint)
│ ↑ scraped by scrape_ntopng.py → DWH
└─ :12056 → GoFlow2 → Kafka 'network-flows' ← Raw flow data
Custom Lua endpoint: /lua/custom_flow_export.lua returns JSON with DPI app name, bytes, packets, AS names for all active flows. Works with --disable-login 1 (no auth).
ntopng Community REST API requires Enterprise license — all /lua/rest/v2/ endpoints return 302. Custom Lua endpoint bypasses this limitation.
DWH Data Sources (ntopng scraper)¶
scrape_ntopng.py runs every 5min:
- Queries
http://127.0.0.1:3004/lua/custom_flow_export.lua - Enriches flows: nDPI app → service name + category (30+ mappings)
- Pushes to DWH
/api/v1/logs(provider=ntopng, category=network_flow) - Metadata includes: ndpi_app, service, service_category, bytes, packets, src_as, dst_as
mikrotik-tools Scheduler (10 jobs)¶
| Job | Schedule | Purpose | Alert |
|---|---|---|---|
| ros-backup | daily 2:00 | Export ROS config → git | Git commit |
| asn-download | daily 2:50 | iptoasn.com DB (529K ranges) | Telegram |
| vn-ip-update | weekly Sun 3:00 | APNIC VN IP → MikroTik | Telegram |
| port-forward-sync | daily 3:30 | Auto VPN exclusion from NAT rules | Telegram |
| firewall-verify | daily 4:00 | PSD + tarpit rules | Log |
| vpn-check | daily 4:30 | Double VPN risk check | Log |
| vpn-tunnel-monitor | every 30s | VPN health + failover | Telegram |
| dhcp-watch | every 1min | New device alert | Telegram |
| port-monitor | every 2min | ether1-4 up/down | Telegram |
| ntopng-scrape | every 5min | ntopng DPI flows → DWH | DWH data |
Alert Rules (vmalert)¶
Located in stacks/monitoring/ros-monitoring/alerts.yml:
| Alert | Severity | Condition |
|---|---|---|
| WandropSpike | critical | WAN firewall drops >100/min |
| QoSCongestion | warning | WAN queue >0 |
| NetflowDown | critical | NetFlow collector down |
| VPNDown | warning | Gluetun VPN down |
| WireguardDown | warning | ROS WireGuard interface down |
| ConnAnomaly | warning | Router connections >2000 |
| CriticalDeviceOffline | critical | IoT/smart device offline |
| ServerOffline | critical | Core server offline |
| ManyDevicesOffline | warning | 8+ devices offline |
| BandwidthHog | warning | Device >500 Mbps |
| CpuSpikeWithConnectionSpike | critical | CPU >60% + high connections |
| CpuSpikeWithTrafficSpike | warning | CPU >70% + WAN near line rate |
| CpuP99Anomaly | warning | CPU P99 >50% for 1h |
| UnraidDiskThrashing | warning | Disk spinup >5x/hour (HDD only) |
| UnraidDiskNeverSpundown | info | Disk spinning 24h+ (HDD only) |
| UnraidDiskFlapping | warning | Disk state change >10x/10min (HDD only) |
| SpinMonitorDown | critical | Spin monitor offline |
Notification Format¶
Mobile-optimized for Telegram:
🟡 /dev/sdd spinning 2d5h
ST12000NM0127 | 🌡️45C
📊 I/O:
• ntfy-bridge ✍️2MB
• caddy 👁️16KB
Disk never enters standby
VPN Configuration¶
See docs/mikrotik-vpn-routing-playbook.md for full 6-layer checklist.
WireGuard (ROS)¶
- Interface:
protonvpn→ ProtonVPN VN6 - Routing: WiFi devices (zone-lan + zone-iot) → VPN, servers + PS5 → WAN
- Failover: distance 2 WAN backup + health monitor script
VPN Exclusions (auto)¶
port-forwardedaddress-list: auto-synced from NAT rules- zone-servers (.10-.32, .59): excluded by whitelist
- Tailscale (100.64.0.0/10): excluded in vpn-route table
- PS5 (.100): auto via port-forwarded list
Health Check¶
12-section verification: RouterOS, ports, WireGuard, VPN routing, firewall, DHCP, Apprise, VM, AdGuard, DWH, scripts, state files.
Pitfalls¶
- Apprise endpoint is
/notify(not/) — POST to/returns 405 - Bot token from Komodo Variable includes
[SECRET]suffix — strip withgrep -oP '= \K\S+' cap_drop: ALLrequirescap_add: [SETUID, SETGID, CHOWN]for apprise supervisord- Bridge fast-forward must be OFF for mangle routing marks to work
- MSS clamping required for protonvpn (TCP SYN hangs without it)
- Forward chain must have
allow-lan-to-vpnrule before catch-all drop - ntopng Community can't receive NetFlow directly — use netflow2ng
- ntopng flows show 0 bytes via netflow2ng — DPI works but bytes pending fix
- AdGuard query log retention increased from 6h to 24h