Skip to content

Tests Network iperf3

Mason Rowe edited this page Oct 31, 2026 · 1 revision

Tests: Network (iperf3)

In short

YABS measures your server's network throughput by running iperf3 against a list of donated public iperf servers around the world — once sending, once receiving, on IPv4 and again on IPv6 if you have it. Each location gets ~10 seconds of traffic per direction with 8 parallel TCP streams. "busy" in the table means the far-end server couldn't be tested, not that your server is slow.

For everyone

iperf3 Network Speed Tests (IPv4):
---------------------------------
Provider        | Location (Link)           | Send Speed      | Recv Speed      | Ping           
-----           | -----                     | ----            | ----            | ----           
Clouvider       | London, UK (10G)          | 940 Mbits/sec   | 935 Mbits/sec   | 8.14 ms        
Eranium         | Amsterdam, NL (100G)      | 938 Mbits/sec   | 942 Mbits/sec   | 11.2 ms        
Uztelecom       | Tashkent, UZ (10G)        | busy            | 312 Mbits/sec   | 98.7 ms        
Leaseweb        | Singapore, SG (10G)       | 255 Mbits/sec   | 289 Mbits/sec   | 182 ms         
Clouvider       | Los Angeles, CA, US (10G) | 301 Mbits/sec   | 344 Mbits/sec   | 141 ms         
Leaseweb        | NYC, NY, US (10G)         | 498 Mbits/sec   | 512 Mbits/sec   | 78.3 ms        
Edgoo           | Sao Paulo, BR (1G)        | 118 Mbits/sec   | 130 Mbits/sec   | 196 ms         
  • Send Speed = your server → the remote server (your "upload"). Recv Speed = remote → your server (your "download"). The direction is named from your server's perspective; the number itself is what iperf3 measured at the receiver end, which is the far end on a send test and your server on a -R receive test. (This trips people up regularly — the maintainer has explained it on LowEndTalk since October 2019.)
  • Judge the nearest locations first. Distant servers are limited by latency and transit, not by your port — a 1 Gbps VPS showing 200–600 Mbit/s to another continent is normal. See Interpreting Results.
  • Ping is a single ICMP echo — a one-sample latency, not a measurement campaign.

Why iperf3 and not something like speedtest.net? From the maintainer, Oct 2019: "Speedtest is a single threaded connection ... with iperf, you'll have many parallel connections providing a better representation of how your server will handle many high-bandwidth connections simultaneously." Eight parallel streams also fill fat-or-long links that a single TCP flow can't.

The server list

Stored in the IPERF_LOCS array, five fields per server: host, port range, provider name, location label (with advertised link), supported modes. Current list (yabs.sh v2026-10-31):

Host Ports Provider Location (link) Modes
lon.speedtest.clouvider.net 5200–5209 Clouvider London, UK (10G) IPv4+IPv6
iperf-ams-nl.eranium.net 5201–5210 Eranium Amsterdam, NL (100G) IPv4+IPv6
speedtest.uztelecom.uz 5200–5209 Uztelecom Tashkent, UZ (10G) IPv4+IPv6
speedtest.sin1.sg.leaseweb.net 5201–5210 Leaseweb Singapore, SG (10G) IPv4+IPv6
la.speedtest.clouvider.net 5200–5209 Clouvider Los Angeles, CA, US (10G) IPv4+IPv6
speedtest.nyc1.us.leaseweb.net 5201–5210 Leaseweb NYC, NY, US (10G) IPv4+IPv6
speedtest.sao1.edgoo.net 9204–9240 Edgoo Sao Paulo, BR (1G) IPv4+IPv6

A commented-out Telia Helsinki entry and an # AFR placeholder comment remain in the array for future fill-ins.

With -r the list shrinks to three: Clouvider London, Leaseweb Singapore, Leaseweb NYC. (Both the README and the code comment above the -r array still name older servers — the array itself is authoritative.)

With -p "host:port_range:name:location:modes[,...]" the default list is replaced entirely. See Command-Line Flags for the exact syntax and the comma/colon pitfalls.

A common use for -p is testing against your own iperf3 server — run iperf3 -s (optionally -p 5201) on a second host, then:

curl -sL yabs.sh | bash -s -- -p "myserver.example.net:5201-5201:Mine:Home Lab (1G):IPv4"

This removes the shared-server variable entirely: no busy, no other users' traffic — the cleanest possible read on your own uplink to a known endpoint. Note the 9204-9240 Edgoo range vs the usual 520x ranges — port range is per-server config, which is why each list entry carries its own.

How a test runs

For each server that advertises the current mode, per direction:

timeout 15 iperf3 -4|-6 -c <host> -p <random-port> -P 8        # send
timeout 15 iperf3 -4|-6 -c <host> -p <random-port> -P 8 -R     # receive

iperf3 flags used — and not used

Flag Value Why
-4 / -6 per pass Forces the address family for the current table.
-c <host> server host Client mode against the public server.
-p <port> random in range Each attempt picks a fresh port (shuf -i) — different ports can land on different daemon slots.
-P 8 8 streams Parallel connections; fills links a single TCP flow can't and better models real multi-connection load.
-R receive pass only Reverses direction — server sends, client receives.
(duration) default 10 s No -t — iperf3's built-in 10-second test.
timeout 15 wrapper External kill so a wedged server can't stall the run.
not used: -u, -b, -t, -J — No UDP tests (so no jitter/loss metrics), no rate caps, no JSON parse — output stays human-format for the table.
flowchart TD
    A[For each server in list] --> B{"supports this IP mode?"}
    B -->|no| A
    B -->|yes| C["send test: pick random port via shuf,<br/>iperf3 -P 8, timeout 15s, up to 3 attempts"]
    C --> D{result contains 'receiver'<br/>and no 'error'?}
    D -->|"yes, speed > 0.00"| E[accept speed]
    D -->|"0.00 or blank"| C
    D -->|"'unable to connect'"| F[give up immediately]
    D -->|other error| C2[sleep 2s, retry]
    C2 --> C
    E --> G[sleep 1s]
    F --> G
    C -->|3 attempts done| G
    G --> H["recv test: same but with -R"]
    H --> I["latency: ping -c1 -> 'xx.x ms' or '--'"]
    I --> J[print row; 'busy' where speed missing/0.00]
    J --> A
Loading

What one successful attempt's raw output looks like (iperf3 client, trimmed):

Connecting to host lon.speedtest.clouvider.net, port 5201
[  5]   0.00-10.00  sec   112 MBytes   941 Mbits/sec  sender
...
[SUM]   0.00-10.04  sec  1.10 GBytes   940 Mbits/sec  receiver

YABS greps for receiver (present only when the test completed), then takes field $6 (940) and $7 (Mbits/sec) from the [SUM] ... receiver line — the combined throughput of all 8 streams measured at the receiving end. error, unable to connect, a blank result, or 0.00 each trigger the retry/give-up logic.

Key details from the code:

  • 3 attempts per direction (not 5 — the README is stale on this; the code says 3, and has since Jun 2022. History: the original Oct 2019 script tried up to 10 times, reduced to 5 in Feb 2021, then to 3 in Jun 2022 — enough to ride out a transient "server busy" without stalling the run). Each attempt picks a fresh random port from the server's range via shuf -i <range> -n 1.
  • Duration is iperf3's default: 10 seconds per run.
  • -P 8 — 8 parallel TCP streams, so the number reflects aggregate multi-connection throughput, not a single flow's ceiling.
  • 15 s timeout wraps each attempt; an unresponsive server doesn't stall the run.
  • Success criteria: output must contain receiver and not error; the parsed speed must be non-blank and not 0.00. unable to connect aborts that direction's retries immediately; other errors retry after a 2 s sleep.
  • busy is printed when a direction ends with a blank/0.00 result after its attempts — meaning the public server had no free test slot or refused. It says nothing about your own capacity. (Public iperf3 daemons accept one test at a time; when someone else is mid-test, yours gets refused.)
  • Success requires the literal word receiver and no error in the output, and the [SUM] ... receiver line's numeric field is what gets reported — exactly as printed, unit included (Mbits/sec / Gbits/sec). The same strings land in the JSON send/recv fields.
  • The whole IPv4 table runs first if IPv4 connectivity was detected, then a second full table for IPv6 — servers advertising only one mode simply don't appear in the other table.

What "good" looks like

On an unthrottled gigabit port to a nearby server, expect numbers in the high hundreds of Mbit/s (~940 Mbit/s is the practical TCP ceiling of 1 Gbps after overhead); a 10G port and 10G+ far end can show ~9.4 Gbit/s. Anything lower to a nearby location invites a closer look; anything lower to a distant location is usually just physics (latency × congestion), not a fault. Detailed interpretation — including asymmetric send/recv — is in Interpreting Results.

Two tables: IPv4 then IPv6

YABS runs the entire server list once per IP version, in order — iperf3 Network Speed Tests (IPv4) then (IPv6) — but only for versions the startup connectivity probe found. The modes field on each server entry (IPv4, IPv6, IPv4|IPv6) filters it out of a pass where it can't serve you, so the two tables can have different row counts.

The ping column uses the same -4/-6 flag as the test it follows — before a Jul 2026 fix (#126) IPv6 servers could get pinged over v4, producing a misleading latency. One ping -c1 is all it is: directionally useful, statistically thin. For real latency analysis use mtr/ping separately.

How much bandwidth a run uses

Each tested server receives ~10 s of line-rate traffic in each direction, per IP version:

  • Per direction: 10 s × link rate. On a 1 Gbps port: 10 s × 1 Gbit/s = 10 Gbit ≈ 1.25 GB.
  • Per server per stack (send + recv): ≈ 2 × 1.25 GB = 2.5 GB on a 1 Gbps port.
  • Full run: 2.5 GB × 7 servers × 2 stacks (IPv4 + IPv6) ≈ 35 GB — the practical ceiling on a 1 Gbps port.
  • On faster ports it scales linearly: a 10 Gbps port can theoretically pull ~125 GB in the same pattern (10 s × 10 Gbit/s = 12.5 GB/direction), though far-end capacity usually intervenes.
  • Retries add more traffic; slow or "busy" servers reduce it.

Observed real-world usage matches the model: ~20–24 GB per full run was reported on LowEndSpirit in Feb 2020. On metered plans use -r (cuts locations 7→3, roughly 57% less iperf traffic — same arithmetic, 2.5 GB × 3 servers × 2 stacks ≈ 15 GB ceiling on 1G) or -i (no network tests).

Time budget, worst case

Each server costs ~20 s when healthy (10 s send + 1 s pause + 10 s recv) and up to ~90 s when every attempt times out (3 × 15 s per direction plus sleeps). A full network section is typically 2–5 minutes; a pathological one (all servers unreachable, full timeout paths on both stacks) can stretch past 15.

Where the latency column comes from

Separately from iperf itself, each row ends with a ping -4|-6 -c1 <host> — one ICMP echo, parsed for time=. It's there because raw throughput without latency is half the story: 300 Mbit/s with 8 ms means a nearby healthy path; the same number with 250 ms is a saturated long-haul one. -- when ping is absent or the echo fails — the throughput numbers are unaffected either way.

Firewalls and busy

Outbound TCP to the iperf port ranges (5200–5210, 9204–9240) must be allowed. A host firewall (e.g. ufw with a restrictive outbound policy) can make every location fail — reported as early as Oct 2019. Corporate egress filters and NAT VPSes behind shared IPv4 produce the same symptom. If all rows say busy, test one server manually:

iperf3 -c lon.speedtest.clouvider.net -p 5201 -P 8

Retry decision table

Exactly what the code does with each attempt outcome:

Attempt result Action
output has receiver, speed parses to non-0.00 accept, move on
output has receiver but speed blank or 0.00 retry (up to 3 total)
output contains unable to connect give up immediately — server refusing, no point retrying
other error output sleep 2 s, retry (up to 3 total)
no output at all / timeout kill counts as error path → retry after 2 s

Between the send and receive phases there's a fixed sleep 1 — a courtesy pause so the server can free its test slot.

Server list history and policy

The list is donated infrastructure — providers volunteer bandwidth and slots. The maintainer's standing policy: changes should be 1:1 swaps that improve geographic/provider diversity, port-range depth, or link capacity; the list should not just grow (bandwidth costs money).

Eras of the list:

  • 2019–2020: Bouygues (bouygues.iperf.fr, the very first server in Oct 2019 — the day-one script tested only that one server), then Online.net, Severius, Worldstream, wilhelm.tel, Biznet, Hostkey, Vultr, Velocity, Airstream, and Hurricane Electric as coverage broadened. When -r was introduced (Feb 2020), its reduced trio was Online.net France, WorldStream NL, and Hurricane Electric US — all long since swapped out.
  • 2021–2023: the Clouvider / Scaleway / Hybula / Uztelecom era — Clouvider London & NYC (Feb 2021), WebHorizon Singapore (Oct 2021), Hybula 40G NL (2022), Scaleway Paris, Uztelecom Tashkent for Central Asia coverage, plus steady churn as servers came and went and ping/latency column (#43) and standardised location naming (#48) landed in Nov 2022.
  • Current: Clouvider (London, LA), Eranium Amsterdam, Uztelecom Tashkent, Leaseweb (Singapore, NYC), Edgoo Sao Paulo.

Parked candidate swaps are tracked in PRs #125 (Alwyzon Vienna, 100G) and #115 (Advin Kansas City, 40G), held as inputs to a future assessment rather than merged.

If you operate a public iperf3 server and want it in the list, the bar is essentially: it replaces a weaker existing entry on geography/provider diversity, port-range depth, or link capacity — not "one more server".

What this test does NOT measure

  • UDP, jitter, packet loss — iperf3 can measure all three (-u), YABS doesn't. TCP goodput only.
  • Single-stream throughput — everything is -P 8. A single-flow ceiling needs a manual iperf3 run.
  • Latency under load — the ping sample runs between tests, not during them; bufferbloat is invisible here.
  • Route quality to your clients — results reflect the path to these specific donated endpoints in these cities, not your users' paths.
  • Sustained transfer — 10 s bursts can sit inside ISP "boost" windows that a 10-minute transfer would blow through.

See also

Clone this wiki locally