12 July 2026

A Triggerable Home LAN Health Check with Systemd

The Claude sessions on my laptop dropped out. Internet felt fine, but I wasn't sure — was it the connection, a specific service, or something deeper on the LAN? I wanted a single command that would tell me everything in one pass: internet reachability, DNS, gateway, key hosts, and all the local services I rely on.

I also had a slightly awkward discovery to handle. I run a TP-Link Kasa HS110 smart plug to control power to one of my Proxmox servers (it has no IPMI, so this is the only remote power switch). The HS110 is on DHCP, and during the check I found it had silently moved from 10.140.1.235 to 10.140.1.236 — which explained why my documented procedures would have failed at the worst possible moment.

The script

A single Python file in agents/lan_health.py:

import subprocess, socket, sys, time, urllib.request, urllib.error, ssl

INTERNET_HOSTS = [("1.1.1.1", "Cloudflare"), ("8.8.8.8", "Google DNS")]
DNS_NAMES      = ["google.com", "cloudflare.com"]

LAN_HOSTS = [
    ("10.140.2.6",   "Gateway"),
    ("10.140.3.82",  "Xenon (PVE)"),
    ("10.140.3.202", "claude-hub"),
    ("10.140.20.1",  "Incus bridge"),
]

HTTP_SERVICES = [
    ("https://open-webui.lan",  "open-webui"),
    ("https://litellm.lan",     "litellm"),
    ("https://searxng.lan",     "searxng"),
    ("https://xenon.lan",       "xenon (Proxmox UI)"),
    # ... add your own
]

HS110_MAC = "D8:47:32:A3:FF:CA"
HS110_IP  = "10.140.1.236"  # DHCP — may drift

It pings the internet hosts (any one responding counts as internet up), resolves a couple of DNS names, hits an HTTPS endpoint to confirm real connectivity, then pings key LAN hosts and does a quick HTTP check against each .lan service. For the smart plug, it tries the known IP first and if that fails it runs an nmap sweep to find the MAC — so it catches a DHCP lease change and tells you the new address.

Output is colour-coded and exits non-zero if anything fails, which plays nicely with scripts and monitoring.

The service

The whole point is to be triggerable on demand rather than always-on. A systemd oneshot unit is the right shape for this:

# /etc/systemd/system/lan-health.service
[Unit]
Description=Home LAN health check
After=network-online.target

[Service]
Type=oneshot
ExecStart=/path/to/venv/python3 /path/to/agents/lan_health.py
User=your-user
StandardOutput=journal+console
StandardError=journal+console

Run it with:

systemctl start lan-health.service

Output goes to the journal (so journalctl -u lan-health.service shows the last run), and also to the console if you're watching. No persistent daemon, no polling — just runs when you ask it to.

What it found

Running this for the first time caught the HS110 DHCP drift immediately. It also surfaced three services returning 502 (the backend processes had stopped but the reverse proxy was still up), which I'd otherwise not have noticed. It's become my first step whenever anything feels off on the LAN.

If you're running any kind of home lab with more than a handful of services, a ten-minute investment in something like this pays off fairly quickly. Adjust the host list to match your setup, add it to systemd, and you're done.

Ta ta for now, Matt

No comments:

Post a Comment