{"id":889,"date":"2026-08-07T20:34:57","date_gmt":"2026-08-07T20:34:57","guid":{"rendered":"https:\/\/anthonyparkes.co.uk\/?p=889"},"modified":"2026-08-07T20:37:19","modified_gmt":"2026-08-07T20:37:19","slug":"building-a-live-wan-dashboard-for-my-home-network","status":"publish","type":"post","link":"https:\/\/anthonyparkes.co.uk\/?p=889","title":{"rendered":"Building a live WAN dashboard for my home network"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;ve had Starlink sitting behind my Virgin Media connection as a failover for a while now, quietly doing its job and just as quietly telling me nothing about when it&#8217;s doing that job. Failover kicks in, the house stays online, and I have no idea it happened unless I go looking. That&#8217;s fine right up until it isn&#8217;t \u2014 until I&#8217;m troubleshooting something else entirely and it turns out I&#8217;ve been on Starlink for six hours and didn&#8217;t know.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So I built a dashboard. It&#8217;s live now at <a href=\"https:\/\/traffic.anthonyparkes.co.uk\" data-type=\"link\" data-id=\"traffic.anthonyparkes.co.uk\">traffic.anthonyparkes.co.uk<\/a>, and this is the story of how it came together.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The starting point<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I didn&#8217;t want a general-purpose network monitoring tool with a dozen panels I&#8217;d never look at twice. I wanted something built around the one question I actually care about \u2014 which line am I on right now, and is it healthy? Everything else got cut before it was ever built.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Getting the data<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My UDM-SE \u2014 &#8220;Cab1&#8221;, if you&#8217;re wondering \u2014 already knows everything I need. It&#8217;s just a matter of asking it politely. SNMP against the router&#8217;s IF-MIB counters gets me interface octet counts for both WAN interfaces, polled every minute, delta&#8217;d against the last reading, and turned into bits-per-second. Nothing exotic, deliberately \u2014 no MIB files, no symbolic OID names, just raw numeric OIDs that work identically on any device you point them at, MIBs installed or not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The failover side needed a different approach, because link-state doesn&#8217;t tell you which connection is actually carrying traffic \u2014 it just tells you both are technically up. So instead: check the public IP. If it starts with my Virgin Media block, I&#8217;m on primary. Anything else, I&#8217;ve failed over. Ping two fixed targets alongside that \u2014 one routed over each path specifically \u2014 and I get independent latency and up-down health for both connections regardless of which one&#8217;s active.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The bug that gave my internet a 20,934% utilisation spike<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At one point the dashboard cheerfully reported that my 900 Megabit circuit had hit 20,934% utilisation. I wish. That would make for an extremely good broadband deal.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What actually happened: a single bad SNMP read produced a counter delta that, once divided out, turned into a number so far beyond physically possible that it should have been an obvious red flag rather than a data point. The fix was a sanity ceiling \u2014 cap any reading against a sane multiple of the interface&#8217;s rated speed, and if it blows past that, throw the sample away rather than trust it. I also had to handle the more sensible cousin of that bug: a genuine counter reset \u2014 device reboot, SNMP agent restart \u2014 shows up as the counter going backwards, which needed its own logic so the collector doesn&#8217;t just get stuck comparing against a stale baseline forever.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fun bug. Not a fun three seconds of graph.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Shipping it<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The whole thing runs as one self-contained Docker container \u2014 PHP and Apache serving the dashboard, cron inside the same container polling both collectors every minute. Code and config are bind-mounted rather than baked into the image, so I can tweak thresholds or fix a label without a rebuild. Data lives in its own mounted volume too, so a container rebuild doesn&#8217;t wipe out weeks of history.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What it actually shows<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two panels, one glance:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Which connection is active right now, and how long it&#8217;s been on that path. Latency and uptime for both, independently, regardless of which one&#8217;s carrying traffic. Capacity and utilisation for each circuit \u2014 daily traffic, peak usage, how much time is spent near the warning and critical thresholds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s it. No fluff, no charts I&#8217;d never look at twice. Just the two questions I actually wanted answered when I started this: what&#8217;s my connection doing, and can I trust it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s public if you want to poke at it \u2014<a href=\"https:\/\/traffic.anthonyparkes.co.uk\" data-type=\"link\" data-id=\"traffic.anthonyparkes.co.uk\"> traffic.anthonyparkes.co.uk<\/a>. I&#8217;m not putting the code up for now, but happy to talk through any of it if you&#8217;re building something similar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve had Starlink sitting behind my Virgin Media connection as a failover for a while now, quietly doing its job and just as quietly telling<\/p>\n","protected":false},"author":2,"featured_media":890,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"template-fullwidth.php","format":"standard","meta":{"footnotes":""},"categories":[21,2],"tags":[],"class_list":["post-889","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-infrastructure","category-projects"],"_links":{"self":[{"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/889","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=889"}],"version-history":[{"count":1,"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/889\/revisions"}],"predecessor-version":[{"id":891,"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/889\/revisions\/891"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=\/wp\/v2\/media\/890"}],"wp:attachment":[{"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=889"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=889"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/anthonyparkes.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=889"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}