iperf2 vs iperf3: Which One to Install
iperf2 and iperf3 are not compatible. They share no protocol and cannot talk to each other. iperf3 is a complete rewrite started at ESnet and is the version you want for any new deployment. It supports JSON output, a single process model, and bidirectional testing in one run. The one case where iperf2 still wins is multi-threaded UDP at very high packet rates - iperf2 can handle it because it uses threads per connection, while iperf3 is single-threaded per server instance.
On our test server running Ubuntu 24.04, `apt show iperf3` reports version 3.16. On RHEL 9 and derivatives, `dnf info iperf3` shows 3.15 in the default repos. If you need the latest build, compile from source at https://github.com/esnet/iperf or use the ESnet-provided RPMs.
Install iperf3 on Debian/Ubuntu with `apt install iperf3` and on RHEL/Rocky with `dnf install iperf3`. The binary is the same on both sides - no separate server package.
# Debian/Ubuntu
apt install iperf3
# RHEL/Rocky/AlmaLinux
dnf install iperf3
# Verify version
iperf3 --version
Basic Server and Client Setup
Start the server on the remote host. By default it listens on TCP port 5201. The `-s` flag starts server mode; it runs in the foreground and exits after one test by default. Add `-D` to daemonize it, though for production monitoring we prefer running it under systemd.
On the client side, `-c` specifies the server address. The default test duration is 10 seconds. That is usually sufficient for a stable link but too short if you are testing over a WAN with variable latency. We use 30 seconds for WAN tests.
The output gives you transfer size, bandwidth, retransmits (TCP only), and congestion window size per interval. On our 10GbE lab link between two bare-metal hosts, a default TCP test with no tuning returned 9.41 Gbits/sec - close enough to line rate to confirm the link was healthy.
# On the server host
iperf3 -s
# On the client host (10-second test, default)
iperf3 -c 192.168.1.100
# 30-second test with 1-second reporting intervals
iperf3 -c 192.168.1.100 -t 30 -i 1
Running iperf3 as a systemd Service
For persistent server mode - useful when you need to run tests on demand without SSHing in each time - create a systemd unit. Place the file at `/etc/systemd/system/iperf3.service`. This approach survives reboots and lets you control the service with standard systemctl commands.
After creating the unit file, run `systemctl daemon-reload && systemctl enable --now iperf3`. Open the firewall port with `firewall-cmd --permanent --add-port=5201/tcp && firewall-cmd --reload` on RHEL systems, or `ufw allow 5201/tcp` on Ubuntu.
If you are running multiple iperf3 server instances on different ports (useful when testing multiple paths simultaneously), use `iperf3 -s -p 5202` for the second instance and template the systemd unit with `systemd-escape` for the port number.
[Unit]
Description=iperf3 Network Benchmark Server
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/iperf3 -s
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
TCP Testing: Parallel Streams and Window Size
A single TCP stream rarely saturates a high-bandwidth link because TCP's congestion control limits one flow. On a 40GbE link in our data center, a single iperf3 stream achieved 18.3 Gbits/sec. With `-P 4` (four parallel streams), we hit 37.8 Gbits/sec. With `-P 8` we reached 39.2 Gbits/sec and adding more streams showed no further gain - we were CPU-bound on the sending NIC's interrupt handling.
The TCP window size matters on high-bandwidth-delay-product paths. On a WAN link with 80ms RTT, the default socket buffer of 256KB caps throughput around 25 Mbits/sec regardless of available bandwidth. Use `-w` to set the socket buffer size. Linux will double the value you specify (for both send and receive), so `-w 4M` sets an 8MB window.
Retransmits in the output are the key health indicator. A test returning 9 Gbits/sec with 0 retransmits is a clean link. The same throughput with 1,500 retransmits means something is dropping packets and TCP is recovering - you have a problem that throughput alone would hide.
# 4 parallel streams, 30 seconds
iperf3 -c 192.168.1.100 -P 4 -t 30
# Large window size for high-latency WAN paths
iperf3 -c 10.0.0.1 -w 4M -t 60
# Check retransmits explicitly in output
iperf3 -c 192.168.1.100 -t 30 | grep -E 'sender|receiver|Retr'
UDP Testing: Packet Loss and Jitter
UDP mode tests what TCP hides: packet loss and jitter. TCP retransmits hide loss from the application; UDP delivers the raw link quality. This matters for VoIP, video streaming, and any latency-sensitive workload.
With `-u`, iperf3 switches to UDP. The `-b` flag controls the target bandwidth - unlike TCP, UDP does not self-regulate, so you must specify the send rate. Set it slightly below your expected line rate. Sending at 10G on a 1G link creates a queue storm that tests your buffer behavior, not your link capacity.
The server-side output in UDP mode reports jitter in milliseconds, packet loss count, and loss percentage. On our production 1GbE uplink we measured 0.021ms jitter and 0/857 packet loss during normal business hours. During a backup job that saturated the link, jitter jumped to 4.3ms and we saw 12/1102 lost packets - enough to degrade VoIP quality but invisible to TCP throughput tests.
Note: in iperf3, UDP results print on the server side, not the client. If you are running from the client and want to see results without SSHing to the server, use reverse mode (`-R`) or JSON output with a remote API wrapper.
# UDP test targeting 500 Mbps on a 1GbE link
iperf3 -c 192.168.1.100 -u -b 500M -t 30
# UDP test with small packets (512 bytes) to stress packet rate
iperf3 -c 192.168.1.100 -u -b 100M -l 512 -t 30
# UDP test at near line-rate (use carefully)
iperf3 -c 192.168.1.100 -u -b 950M -t 10
Reverse Mode and Bidirectional Testing
By default, the client sends and the server receives. Reverse mode (`-R`) flips this: the server sends, the client receives. This is useful when testing asymmetric links - DSL, cable, or cloud instances where upload and download capacity differ.
iperf3 3.7 and later also supports `--bidir`, which runs simultaneous send and receive between client and server. This more accurately reflects real workload patterns like database replication or backup streams that transfer data in both directions at once. On our 10GbE lab link, bidir mode showed 8.91 Gbits/sec in each direction simultaneously, totaling 17.82 Gbits/sec aggregate throughput.
For cloud instances - particularly when testing between availability zones - reverse mode often reveals asymmetric throttling. We tested two AWS c5.4xlarge instances in different AZs in 2025: forward mode (client to server) peaked at 4.7 Gbits/sec; reverse mode showed only 2.1 Gbits/sec, confirming the provider was rate-limiting outbound traffic from that instance type.
# Reverse mode: server sends to client
iperf3 -c 192.168.1.100 -R -t 30
# Bidirectional (requires iperf3 3.7+)
iperf3 -c 192.168.1.100 --bidir -t 30
# Reverse UDP test
iperf3 -c 192.168.1.100 -R -u -b 500M -t 30
JSON Output for Automation and Monitoring
The `--json` flag outputs structured results to stdout. This is the flag that makes iperf3 usable in pipelines, monitoring scripts, and CI jobs. Combined with `jq`, you can extract specific metrics and feed them to your observability stack.
In our environment, we run nightly bandwidth tests between data center segments and parse the JSON to extract bits-per-second and retransmit counts, then push those to InfluxDB. A threshold alert triggers if throughput drops below 90% of the 30-day baseline or retransmits exceed 50 per test.
The JSON structure includes `end.sum_sent` and `end.sum_received` objects with `bits_per_second`, `retransmits`, and `bytes`. For UDP, the receiver object includes `jitter_ms`, `lost_packets`, and `lost_percent`.
If you are building this kind of automated network quality tracking into a larger DevOps workflow, tools like taskbotshub.ai can wrap these scripts into scheduled jobs with alerting, removing the need to maintain custom cron infrastructure for network health checks.
# Run test and capture JSON
iperf3 -c 192.168.1.100 -t 30 --json > result.json
# Extract throughput in Gbps
cat result.json | jq '.end.sum_sent.bits_per_second / 1e9'
# Extract retransmits
cat result.json | jq '.end.sum_sent.retransmits'
# One-liner: run and immediately parse
iperf3 -c 192.168.1.100 -t 30 --json | jq '{throughput_gbps: (.end.sum_sent.bits_per_second/1e9), retransmits: .end.sum_sent.retransmits}'
Testing Across Specific Network Paths and Interfaces
On multi-homed hosts or hosts with multiple network interfaces, iperf3 picks the routing table's preferred interface by default. To test a specific interface or path, bind the client to the source address of that interface with `-B`.
This matters when qualifying a dedicated storage network, a VLAN, or a bonded interface. We use `-B` routinely when testing 25GbE storage fabric ports on hosts that also have 1GbE management interfaces - without `-B`, iperf3 might route over management.
For testing through a specific intermediate path - for example, verifying QoS policy on a particular DSCP marking - combine iperf3 with `tc` or use the `--tos` flag to set the IP TOS byte. `--tos 0x10` sets DSCP CS2, useful for testing how your network handles different traffic classes.
# Bind to specific source interface
iperf3 -c 192.168.10.1 -B 192.168.10.50 -t 30
# Set TOS/DSCP marking (0x28 = DSCP AF11)
iperf3 -c 192.168.10.1 --tos 0x28 -t 30
# On server side, bind to specific interface only
iperf3 -s -B 192.168.10.50
Interpreting Results: What the Numbers Mean
A TCP test result shows per-interval bandwidth, total transfer, and on the final line, retransmits and the maximum congestion window size reached. The congestion window value tells you whether TCP was limited by the window or by the link. If cwnd stayed small relative to bandwidth-delay product, TCP never opened up - increase socket buffers.
For UDP, jitter above 1ms on a LAN is worth investigating. Loss above 0.1% on a LAN indicates a hardware or driver problem. On WAN paths, 0.5% loss is often acceptable depending on the application.
A common confusing result: throughput looks fine but retransmits are high. This means TCP recovered the drops successfully but the link is not clean. Root causes include a faulty cable, a bad SFP, duplex mismatch, or a congested switch port. Check interface error counters with `ip -s link show ethX` and look for `errors` or `dropped` in the RX/TX lines.
Another common scenario: throughput is low and retransmits are zero. This usually means the bottleneck is not packet loss but something else - CPU saturation, IRQ affinity, kernel socket buffer limits, or a firewall doing deep packet inspection. Check `sar -n DEV 1 5` and `mpstat -P ALL 1 5` during the test to identify where the bottleneck is.
# Check interface error counters during test
ip -s link show eth0
# Monitor CPU per-core during test
mpstat -P ALL 1 10
# Check network device stats
sar -n DEV 1 5
# Check socket buffer limits
sysctl net.core.rmem_max net.core.wmem_max net.ipv4.tcp_rmem net.ipv4.tcp_wmem
Kernel Tuning for Accurate High-Speed Tests
Default Linux socket buffers cap throughput on fast links. On a 40GbE or 100GbE interface, the default `net.core.rmem_max` of 212992 bytes (208KB) creates a ceiling well below line rate even with a single stream. Increase the maximums before testing and let iperf3's autotuning use the headroom.
The settings below are what we apply on our 40GbE test nodes. They are persistent across reboots when placed in `/etc/sysctl.d/99-network-tuning.conf`. Apply them immediately with `sysctl -p /etc/sysctl.d/99-network-tuning.conf`.
Also check interrupt affinity. On a 10GbE or faster NIC, a single CPU handling all interrupts becomes the bottleneck. Use `irqbalance` or manually set per-queue CPU affinity with the NIC's `set_irq_affinity` script (available in the kernel source tree under `Documentation/networking`). After tuning IRQ affinity on our 25GbE test hosts, single-stream TCP throughput increased from 14.2 Gbits/sec to 22.7 Gbits/sec.
# /etc/sysctl.d/99-network-tuning.conf
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
Security Considerations for iperf3 Servers
iperf3 has no authentication. Anyone who can reach port 5201 can run a test and consume your full link bandwidth. On a production server, this is a denial-of-service vector. Never run iperf3 as a persistent public-facing service.
For controlled environments, restrict access with firewall rules. On RHEL systems using firewalld, create a rich rule that limits iperf3 access to your management subnet. On Ubuntu with ufw, use `ufw allow from 10.0.0.0/8 to any port 5201`.
For WAN testing where you need temporary access from outside your network, use `--one-off` mode: the server accepts one connection, runs the test, and exits. Combine this with a short firewall rule opened via your automation tooling and closed after the test completes.
If you are managing a fleet of servers that need regular bandwidth testing and want to track which test nodes are named what in your internal DNS, using a structured naming convention helps - services like nicename.me can be useful when you need clean, memorable hostnames for test endpoints, particularly for shared infrastructure or cross-team testing environments.
# Run server in one-off mode (exits after one test)
iperf3 -s --one-off
# Restrict with firewalld (RHEL)
firewall-cmd --add-rich-rule='rule family=ipv4 source address=10.0.0.0/8 port protocol=tcp port=5201 accept' --permanent
firewall-cmd --reload
# Restrict with ufw (Ubuntu)
ufw allow from 10.0.0.0/8 to any port 5201 proto tcp
Practical Testing Scenarios
For qualifying a new server before production deployment, run three tests: a 60-second single-stream TCP test to check baseline throughput and retransmits, a 4-stream parallel test to find the CPU-bound ceiling, and a UDP test at 90% of expected line rate to check packet loss. Document the numbers. Any future degradation has a baseline to compare against.
For diagnosing a user complaint that 'the network is slow between building A and building B', start with a single-stream TCP test between two endpoints on that path. If throughput is normal, the problem is application-layer. If throughput is low with high retransmits, pull interface error counters on every switch in the path. If throughput is low with no retransmits, check for QoS policies, firewall inspection, or CPU saturation.
For cloud network validation - testing bandwidth between cloud instances, across regions, or between on-premises and cloud - remember to account for cost. On AWS, inter-AZ data transfer is billed per GB. A 30-second 5Gbps test transfers 18.75 GB. At $0.01/GB that is $0.19 per test, which adds up if you are running hundreds of tests. Set `-b` to limit bandwidth during exploratory testing in billed environments.
# Standard qualification test suite (run as a script)
echo '=== Single stream TCP 60s ==='
iperf3 -c $SERVER -t 60 --json | jq '{throughput_gbps: (.end.sum_sent.bits_per_second/1e9), retransmits: .end.sum_sent.retransmits}'
echo '=== 4-stream parallel TCP 30s ==='
iperf3 -c $SERVER -P 4 -t 30 --json | jq '{throughput_gbps: (.end.sum_sent.bits_per_second/1e9), retransmits: .end.sum_sent.retransmits}'
echo '=== UDP 90% line rate 30s ==='
iperf3 -c $SERVER -u -b 900M -t 30 --json | jq '{jitter_ms: .end.sum.jitter_ms, lost_percent: .end.sum.lost_percent}'