My "LAN speed test" was actually an internet speed test

I'd just replaced a faulty LAN cable, so when iperf3 said 30 Mbit/s I blamed the new one. The traffic had never touched the LAN.

On this page
  1. Attempt 1: timed out
  2. The clue I'd left out
  3. Attempt 2: about 30 Mbit/s
  4. Checking the actual LAN link
  5. Takeaways

A few days ago, while updating my homelab server to the latest Arch Linux packages and installing CUDA for ExamX, I found that its LAN cable was faulty. The link had fallen back to 10 Mbit/s. I swapped in a Cat6 cable and the link came back up at 1000 Mbit/s.

A link speed is only what the two ends agreed on, though, not what actually gets through. I wanted to know how fast the network really is, so I tested it with the standard tool is iperf3: run a server on one end and a client on the other, and it measures raw throughput with the disks out of the picture.

iperf3 -s                    # on the server, listens on port 5201
iperf3 -c <server-ip>        # on the client
iperf3 -c <server-ip> -R     # reverse direction: server sends to the client

Attempt 1: timed out

I pointed my Windows PC at the server's wired LAN address and got:

iperf3: error - unable to connect to server - server may have stopped running
or use a different port, firewall issue, etc.: Connection timed out

My first thought was the firewall. But ping to the same address timed out too, so this wasn't about iperf3 or a port at all. The PC couldn't reach that address.

The clue I'd left out

Then I mentioned something I hadn't thought to say: I was talking to Claude over SSH from that same PC. If SSH works but the LAN address doesn't, the SSH session must be taking a different path. Claude checked where the session was coming from: a Tailscale address. The PC wasn't on my home LAN at all. It was reaching the server through the Tailscale tunnel.

Attempt 2: about 30 Mbit/s

Pointed at the server's Tailscale address, iperf3 connected and reported about 30 Mbit/s. That's terrible for gigabit Ethernet, and with the old cable fresh in my mind, I assumed the new one was bad too.

It wasn't. tailscale status showed a direct connection (not relayed through a DERP server), with the PC at a public IP on a different network about 10 ms away. So the test measured:

the slower of the PC's download speed and the server's home upload speed, minus a little WireGuard overhead.

30 Mbit/s is a normal home upload speed. The number was right. I was just measuring something other than what I thought.

That still left the question I'd started with: was the new cable any good? Claude ran a few checks from the server towards the router:

  • Link speed: ethtool reported 1000Mb/s, so the Cat6 cable and router port negotiated gigabit. A damaged cable usually falls back to 100 Mbit/s, or all the way to 10 like my old one did.
  • Latency to the router: about 0.8 ms average, with 0% packet loss.

The new cable is fine. Measuring the link's real throughput needs a second wired device on the same LAN running iperf3 -s, since most routers can't act as an iperf3 endpoint.

Takeaways

  • A timeout on both ping and the app means a reachability problem, not a port or firewall problem in the app.
  • Know which path your traffic takes. With Tailscale running, "the server's address" can mean a LAN IP, a tailnet IP or something routed in between. Check with tailscale status, and echo $SSH_CLIENT shows where an SSH session is coming from.
  • Over a VPN, iperf3 measures the internet. Over the internet, home upload speed is almost always the bottleneck.
  • ethtool <iface> | grep Speed is the 5-second cable check. A faulty cable usually shows up as a link stuck at 100 or 10 Mbit/s. Check it before you blame the cable.