Image on Docker Hub hub.docker.com/r/networkstatic/iperf3/
docker run -it --rm -p 5201:5201 networkstatic/iperf3 --help
To test bandwidth between two containers, start a server (listener) and point a client container (initiator) at the server.
Start a listener service on port 5201 and name the container "iperf3-server":
docker run -it --rm --name=iperf3-server -p 5201:5201 networkstatic/iperf3 -s
That returns an iperf3 process bound to a socket waiting for new connections:
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
First, get the IP address of the new server container you just started:
docker inspect --format "{{ .NetworkSettings.IPAddress }}" iperf3-server
(Returned) 172.17.0.163
Next, initiate a client connection from another container to measure the bandwidth between the two endpoints.
Run a client container pointing at the server service IP address.
Note if you are new to Docker, the --rm flag will destroy the container after the test runs. I also left out explicitly naming the container on the client side since I don't need its IP address. I typically explicitly name containers for organization and to maintain a consistent pattern.
docker run -it --rm networkstatic/iperf3 -c 172.17.0.163
And the output is the following:
Connecting to host 172.17.0.163, port 5201
[ 4] local 172.17.0.191 port 51148 connected to 172.17.0.163 port 5201
[ ID] Interval Transfer Bandwidth Retr Cwnd
[ 4] 0.00-1.00 sec 4.16 GBytes 35.7 Gbits/sec 0 468 KBytes
[ 4] 1.00-2.00 sec 4.10 GBytes 35.2 Gbits/sec 0 632 KBytes
[ 4] 2.00-3.00 sec 4.28 GBytes 36.8 Gbits/sec 0 1.02 MBytes
[ 4] 3.00-4.00 sec 4.25 GBytes 36.5 Gbits/sec 0 1.28 MBytes
[ 4] 4.00-5.00 sec 4.20 GBytes 36.0 Gbits/sec 0 1.37 MBytes
[ 4] 5.00-6.00 sec 4.23 GBytes 36.3 Gbits/sec 0 1.40 MBytes
[ 4] 6.00-7.00 sec 4.17 GBytes 35.8 Gbits/sec 0 1.40 MBytes
[ 4] 7.00-8.00 sec 4.14 GBytes 35.6 Gbits/sec 0 1.40 MBytes
[ 4] 8.00-9.00 sec 4.29 GBytes 36.8 Gbits/sec 0 1.64 MBytes
[ 4] 9.00-10.00 sec 4.15 GBytes 35.7 Gbits/sec 0 1.68 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-10.00 sec 42.0 GBytes 36.1 Gbits/sec 0 sender
[ 4] 0.00-10.00 sec 42.0 GBytes 36.0 Gbits/sec receiver
iperf Done.
Or you can do something fancier in a one liner like so (docker ps -ql returns the CID e.g. container ID of the last container started which would be the server we want in this case)
docker run -it --rm networkstatic/iperf3 -c $(docker inspect --format "{{ .NetworkSettings.IPAddress }}" $(docker ps -ql))
Connecting to host 172.17.0.193, port 5201
[ 4] local 172.17.0.194 port 60922 connected to 172.17.0.193 port 5201
[ ID] Interval Transfer Bandwidth Retr Cwnd
[ 4] 0.00-1.00 sec 4.32 GBytes 37.1 Gbits/sec 0 877 KBytes
[ 4] 1.00-2.00 sec 4.28 GBytes 36.7 Gbits/sec 0 1.01 MBytes
[ 4] 2.00-3.00 sec 4.18 GBytes 35.9 Gbits/sec 0 1.01 MBytes
[ 4] 3.00-4.00 sec 4.23 GBytes 36.3 Gbits/sec 0 1.13 MBytes
[ 4] 4.00-5.00 sec 4.20 GBytes 36.1 Gbits/sec 0 1.27 MBytes
[ 4] 5.00-6.00 sec 4.19 GBytes 36.0 Gbits/sec 0 1.29 MBytes
[ 4] 6.00-7.00 sec 4.17 GBytes 35.8 Gbits/sec 0 1.29 MBytes
[ 4] 7.00-8.00 sec 4.17 GBytes 35.8 Gbits/sec 0 1.29 MBytes
[ 4] 8.00-9.00 sec 4.17 GBytes 35.8 Gbits/sec 0 1.29 MBytes
[ 4] 9.00-10.00 sec 4.22 GBytes 36.3 Gbits/sec 0 1.29 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-10.00 sec 42.1 GBytes 36.2 Gbits/sec 0 sender
[ 4] 0.00-10.00 sec 42.1 GBytes 36.2 Gbits/sec receiver
iperf Done.
You can use a public iperf3 server to measure your Internet speed
docker run -it --rm networkstatic/iperf3 -c ams.speedtest.clouvider.net -p 5208 -R -P15
The downstream speed is in the last line:
[SUM] 0.00-10.00 sec 2.72 GBytes 2.34 Gbits/sec receiver
So in this case: 2.34 Gbits/sec
The same Compose file works with both Podman and Docker. To run an iperf3 server as a long-running Compose service, use:
version: "3"
services:
iperf3:
image: networkstatic/iperf3:latest
restart: always
ports:
- "5201:5201"
command: -sStart it with Docker Compose:
docker compose up -dOr with Podman Compose:
podman compose up -dThe server listens on TCP port 5201 and can be stopped with the matching
compose down command.
The image automation is defined in .github/workflows/build-image.yml as the iPerf3 image CI workflow. It runs when code is pushed to master, for pull requests targeting master, on demand through the GitHub Actions Run workflow button, and on the monthly schedule described below.
Each run checks out the repository on an ubuntu-latest runner, enables QEMU for cross-platform builds, and configures Docker Buildx. The Dockerfile in the repository root is used as the build context. Buildx produces a multi-architecture image for:
linux/amd64linux/arm64
For events other than pull requests, the workflow logs in to Docker Hub with the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN repository secrets and publishes the image as networkstatic/iperf3:latest. Pull-request runs still build both architectures, but do not log in or push an image.
The workflow also has a monthly publishing job through this GitHub Actions cron schedule:
0 0 1 * *
That schedule runs at 00:00 UTC on the first day of every month. It rebuilds both architectures and publishes the resulting manifest to the latest tag. The scheduled run is independent of source changes, so it provides a regular refresh of the published image using the current repository state.
Thanks to ESNET for re-rolling iperf from the ground up. It is an essential tool for NetOps.