Test with the command line
In this test you can set the stream count to match what Elastic Disaster Recovery actually does. Except where a step says otherwise, run this task on the source server, against the endpoint from Install the endpoint software.
Important
Use five concurrent streams. The Elastic Disaster Recovery agent's replication channel is exactly five TCP sockets to one replication server. Five streams reproduces that socket count. A single-stream test measures something Elastic Disaster Recovery never does and can report a fast link as much slower than it is.
Five streams are not equivalent to what the agent does with them. The agent sends one ordered stream and rotates successive chunks across the five sockets from a single sender, and the receiver reassembles them in strict order, so a socket that stalls stalls the whole channel. LibreSpeed runs five independent streams that cannot block one another. Treat the result as the path's upper limit. See Interpreting the result.
Important
Five parallel streams can saturate the source server's uplink for about a minute. On a production source server, that can slow business traffic and raise replication lag on other servers that replicate over the same link. Run the test off-peak, coordinate with whoever operates the network, and test one source server at a time.
Install the client
The client is librespeed/speedtest-cli
export LIBRESPEED_CLI_VERSION=1.0.14 export CLI_ARCH=linux_amd64 # see the release page for other platforms export PORT=1500 # must match the port the endpoint listens on export STREAMS=5 # matches the DRS agent; do not change without reading this task export DURATION=30 # seconds per direction mkdir -p ~/bwclient && cd ~/bwclient curl -sSLO "https://github.com/librespeed/speedtest-cli/releases/download/v${LIBRESPEED_CLI_VERSION}/librespeed-cli_${LIBRESPEED_CLI_VERSION}_${CLI_ARCH}.tar.gz"
Note
The project's GitHub release page lists a SHA-256 digest beside each archive. The digest covers the archive, not the extracted binary, so hash the archive and compare it with that digest before you unpack it:
sha256sum "librespeed-cli_${LIBRESPEED_CLI_VERSION}_${CLI_ARCH}.tar.gz"
tar -xzf "librespeed-cli_${LIBRESPEED_CLI_VERSION}_${CLI_ARCH}.tar.gz" librespeed-cli chmod +x librespeed-cli ./librespeed-cli --version
Note
The release page carries builds for other operating systems and architectures. The
Windows builds are .zip archives, such as
librespeed-cli_1.0.14_windows_amd64.zip, that contain
librespeed-cli.exe. For Windows, see
Install on
Windows
Note
The published Linux client binaries for x86 and Arm are UPX-compressed, which some antivirus and endpoint detection and response (EDR) tools flag or quarantine as a heuristic. If that happens on a production source server, the download is not corrupt: add an exception for the binary, or run the client from a non-production host on the same network path.
Copy the certificate to the client
Prefer real certificate verification over a bypass: copy the endpoint's certificate to the
client and pass it as the trust anchor. This is the better option because the certificate is its
own CA (CA:TRUE in Generate the TLS certificate), so verification
genuinely checks that you are talking to the endpoint you built. A bypass checks nothing and
also hides a TLS-intercepting middlebox, which is one of the things this test exists to
detect.
Display the certificate on the endpoint instance, and copy its full output:
cat ~/bwtest/certs/server.crt
On the source server, write the copied certificate to
~/bwclient/target-ca.crt with the following command. Paste the certificate,
including the -----BEGIN CERTIFICATE----- and
-----END CERTIFICATE----- lines, then press Ctrl+D on an empty line to
end the input:
cat > ~/bwclient/target-ca.crt
The certificate is public material, so it is safe to display and paste; only the private key is secret, and it never leaves the instance. The paste travels over the shell session you already use, not over the measured TLS path on port 1500, so a TLS-intercepting middlebox on that path cannot substitute it. A paste error makes verification fail rather than pass silently.
Note
If you genuinely cannot move the file, for example on an air-gapped source server with no
SSH and no Session Manager path, substitute --skip-cert-verify for
--ca-cert in Run the measurement. The client then accepts any
certificate, including one presented by something intercepting the
connection, and TLS interception is one of the conditions this test is meant to expose.
Write the server list
The client takes its target from a small JSON file. Field names are case-sensitive: note
dlURL, ulURL, and pingURL, but getIpURL with
a lowercase p.
ENDPOINT_HOST=ENDPOINT_NAME# the CN= value the certificate step printed; over VPN or Direct Connect, its PRIV_IP value instead cat > ~/bwclient/servers-1500.json <<JSON [ { "id": 1, "name": "DRS bandwidth test endpoint (TLS :${PORT})", "server": "https://${ENDPOINT_HOST}:${PORT}/backend/", "dlURL": "garbage", "ulURL": "empty", "pingURL": "empty", "getIpURL": "getIP" } ] JSON cat ~/bwclient/servers-1500.json
Important
The trailing /backend/ on server is required. This endpoint
serves its throughput routes under backend/, not at the root, and it has no
.php route aliases. Without the suffix, every endpoint returns 404 and the client
aborts at its pre-flight check, reporting that the server is not responding, before taking any
measurement.
The host in server must match a SAN entry of the same kind: a DNS name must
match a DNS: entry, and a bare IP address must match an IP Address:
entry. In Generate the TLS certificate on the endpoint, the echo "CN=$CN"
line printed a value; set ENDPOINT_HOST on this source server to that same value and
the kinds match automatically.
The host must also match the path you intend to measure. A source
server on-premises over VPN or Direct Connect has to use the endpoint's private IP literal, the
PRIV_IP value from the same step, which the SAN lists as an IP Address:
entry. On-premises DNS usually cannot resolve the private DNS name. The public DNS name
resolves to the public address, which bypasses the tunnel.
An in-VPC client measuring the internet path has to use the endpoint's public IP literal here, because the public DNS name resolves to the private address from inside the VPC and the traffic would never leave.
Run the measurement
cd ~/bwclient ./librespeed-cli \ --local-json servers-1500.json \ --server 1 \ --concurrent "$STREAMS" \ --duration "$DURATION" \ --ca-cert target-ca.crt \ --no-icmp
-
--concurrent 5matches the Elastic Disaster Recovery agent's five replication sockets. Without this flag, the client uses three. -
--duration 30sets the length of each direction, not of the whole run. Expect about 62 seconds of wall-clock time: 30 seconds of download, then 30 of upload, plus pre-flight. -
--no-icmpskips the ICMP ping probe, which many networks block regardless of TCP reachability. The client can still print aPingline measured over HTTP, so a reported ping value does not mean ICMP was used.
Check that the client reports a client IP that is the source server's address, which proves
the request really originated off-instance. Verification succeeded if the run completed
without a certificate error and without --skip-cert-verify.
Run the tests one at a time unless you specifically want to measure contention.
Note
Cross-Availability Zone paths add latency and are metered differently from same-Zone paths, so a figure measured within one Availability Zone should not be assumed to carry over to a cross-Zone path. See Interpreting the result.