Intermittent broadband faults are annoying because they are both obvious and hard to prove.

When the internet disappears during a call or a movie, everyone in the house knows it happened. But by the time the provider asks for evidence, the connection is back, the router status page looks normal, and the conversation slides toward the usual script: reboot the router, check the WiFi, wait and see.

My issue looked like the ONT going down periodically. Connectivity would disappear, then recover after roughly 10-20 minutes. I needed precise timings for Sky and Openreach, not a vague memory of “it was bad again this morning”.

The useful witness turned out to be the Raspberry Pi already sitting on the home network.

Why the Pi Was Useful

I run a Raspberry Pi 5 at home for OpenClaw, CCTV, Frigate, and a few small monitoring jobs. It is boringly always-on, which is exactly what you want for network evidence.

The Pi had two useful properties:

  • it was inside the affected home network
  • it already maintained an outbound tunnel to my VPS

That meant it could tell the difference between several failure modes:

  • the Pi itself is offline
  • the local router is unreachable
  • the wider internet is unreachable
  • my VPS route is unreachable
  • everything is fine and only a particular service is broken

That distinction matters. If you only log “internet down”, the provider can reasonably ask whether it was WiFi, the router, DNS, or your device. If the local gateway is reachable but public probes fail, the evidence is much stronger.

The Topology

The home network currently looks like this from the Pi:

Openreach fibre
    ↓
ONT
    ↓
Sky router
    192.168.0.1
    DHCP / default gateway / DNS
    ↓
TP-Link Deco
    192.168.0.17
    WiFi / mesh coverage
    ↓
Raspberry Pi 5
    192.168.0.23

The monitoring probes are deliberately simple:

sky_router  -> 192.168.0.1
deco_router -> 192.168.0.17
internet    -> 1.1.1.1
vps         -> your-vps.example

There is also one expected noisy event: the home router intentionally reboots every night at about 03:00. I treat outage events starting from 03:00 through 03:29 as scheduled router reboot noise, not fault evidence.

The Evidence Pattern

The important log entries were not just “down”. They showed what stayed reachable and what disappeared.

For example, before I added the separate Deco probe, the monitor saw events like this:

2026-06-13T09:13:34+01:00 status=wan_down gateway=up internet=down vps=down
2026-06-13T09:19:00+01:00 status=up gateway=up internet=up vps=up

2026-06-13T09:23:05+01:00 status=wan_down gateway=up internet=down vps=down
2026-06-13T09:31:33+01:00 status=up gateway=up internet=up vps=up

2026-06-15T02:08:21+01:00 status=wan_down gateway=up internet=down vps=down
2026-06-15T02:19:09+01:00 status=up gateway=up internet=up vps=up

That is the useful shape: the router was reachable, but external connectivity was not.

After the topology was clarified, I added separate probes for the Sky router and the Deco. That made the log more explicit:

2026-06-15T21:06:55+01:00 status=sky_down sky_router=down deco_router=up internet=up vps=up
2026-06-15T21:07:10+01:00 status=up sky_router=up deco_router=up internet=up vps=up

This is a different kind of event. The wider internet was fine, the Deco answered, but the Sky router missed a probe. I would not use that as ONT evidence. It is local topology evidence.

The more compelling broadband fault entries were the WAN-down windows where the local router still answered but internet/VPS probes failed.

That gave me something concrete to send to the provider: exact timestamps, durations, and probe states.

The result: the ONT replacement was taken seriously, and after the provider work the connection has been more stable. That is the whole point of home monitoring. Not dashboards for their own sake. Evidence when reality needs defending against a support flowchart.

The Monitor Script

The Pi-side monitor is intentionally plain Bash. It pings a few targets every 15 seconds and only writes to the log when the state changes.

#!/usr/bin/env bash
set -u

LOG=/home/timur/internet-outage-monitor.log
STATE=/home/timur/.internet-outage-monitor.state
INTERVAL=15

SKY_ROUTER=${SKY_ROUTER:-192.168.0.1}
DECO_ROUTER=${DECO_ROUTER:-192.168.0.17}
TARGET=${TARGET:-1.1.1.1}
VPS=${VPS:-your-vps.example}

status="unknown"
[ -f "$STATE" ] && status=$(cat "$STATE" 2>/dev/null || echo unknown)

log_line() {
  printf "%s %s\n" "$(date -Is)" "$*" >> "$LOG"
}

check_host() {
  ping -n -c 1 -W 2 "$1" >/dev/null 2>&1
}

while true; do
  sky=down; deco=down; internet=down; vps=down
  check_host "$SKY_ROUTER" && sky=up
  check_host "$DECO_ROUTER" && deco=up
  check_host "$TARGET" && internet=up
  check_host "$VPS" && vps=up

  new="down"
  if [ "$sky" = up ] && [ "$deco" = up ] && [ "$internet" = up ]; then
    new="up"
  elif [ "$sky" = up ] && [ "$internet" = up ]; then
    new="deco_down"
  elif [ "$sky" = up ]; then
    new="wan_down"
  elif [ "$deco" = up ]; then
    new="sky_down"
  else
    new="lan_down"
  fi

  if [ "$new" != "$status" ]; then
    log_line "status=$new sky_router=$sky deco_router=$deco internet=$internet vps=$vps sky_router_host=$SKY_ROUTER deco_router_host=$DECO_ROUTER target=$TARGET vps_host=$VPS"
    printf "%s" "$new" > "$STATE"
    status="$new"
  fi

  sleep "$INTERVAL"
done

The script is not clever. That is the feature.

It avoids DNS by probing IPs directly. It writes append-only text. It stores the last state so the log records transitions instead of spamming a line every 15 seconds.

Running It With systemd

On the Pi, install it somewhere boring:

install -d -m 0755 /home/timur/bin
install -m 0755 internet-outage-monitor.sh /home/timur/bin/internet-outage-monitor.sh

Then create a systemd service:

[Unit]
Description=Internet outage monitor
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=timur
ExecStart=/home/timur/bin/internet-outage-monitor.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now internet-outage-monitor.service

Check the log:

tail -f /home/timur/internet-outage-monitor.log

Daily Summary

Raw logs are good for evidence, but not for daily attention. I also have a small Python report that reads the Pi log over the reverse SSH tunnel and posts a daily summary.

It classifies events into:

  • expected 03:00-03:30 router reboot events
  • suspicious outages outside that window
  • current probe state
  • last monitor transition

The useful part is the daily headline:

Pi connectivity daily check
Window: last 24h, generated 2026-06-15 08:00 BST
Current probe: sky_router=up deco_router=up internet=up vps=up
Attention: 3 outage(s) outside 03:00-03:30, total 19m52s

That is much easier to scan than a raw log file.

What This Does Not Prove

This setup does not magically prove every physical detail.

The Pi is on WiFi, so a bad Pi WiFi connection could create false positives. That is why the script checks the local router and the Deco separately. If the local path is down too, I do not treat that as clean WAN evidence.

It also does not inspect the ONT directly. It infers the likely fault from reachability:

  • local router reachable
  • public internet unavailable
  • VPS unavailable
  • repeated pattern around the same kind of failure
  • recovery without local intervention

That is still useful evidence. It narrows the problem enough that the provider conversation becomes about outage windows and line equipment, not whether I remembered to reboot the router.

The Lesson

A Raspberry Pi on the home network is a cheap, boring witness.

It does not need Prometheus, Grafana, InfluxDB, or a carefully curated wall of green graphs. For this job, a 15-second ping loop plus timestamped transition logs was enough.

The important design choices were:

  • probe more than one layer
  • log state transitions, not constant noise
  • keep expected maintenance windows separate
  • preserve exact timestamps
  • make the output easy to send to the provider

When broadband fails intermittently, the hardest part is often not fixing it. It is proving that the failure exists in a way the provider can act on.

The Pi did that job.