Switching a VPS to BBR is a two-line change that people either swear by or claim does nothing. The truth is measured: BBR helps most on links with loss and latency, and changes almost nothing on a clean low-latency LAN. This is a methodology to decide for your VPS instead of copying a blog post.
Prerequisites and the congestion control modules
sysctl net.ipv4.tcp_available_congestion_control
# default on modern kernels includes bbr
lsmod | grep tcp_bbr || modprobe tcp_bbr
sysctl -w net.core.default_qdisc=fq
sysctl -w net.ipv4.tcp_congestion_control=bbr
# persist in /etc/sysctl.d/99-bbr.conffq (fair queueing) is the pacing qdisc BBR depends on. Without it BBR still works but loses its pacing advantage. Verify the effective setting after applying:
sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc
# net.ipv4.tcp_congestion_control = bbr
# net.core.default_qdisc = fqDesign the test before you touch anything
- Fix variables: same client, same server, same time of day, same payload size.
- Test both directions separately (upload and download).
- Measure at least three metrics: throughput, RTT, and retransmits.
- Repeat each run 5x and take the median — single runs are noise.
For a fair comparison, benchmark the same link twice: once with CUBIC, once with BBR, changing only tcp_congestion_control.
# server side: keep iperf3 listening
iperf3 -s
# client, CUBIC run (revert first)
sysctl -w net.ipv4.tcp_congestion_control=cubic
iperf3 -c SERVER_IP -t 30 -R --json > cubic_reverse.json
# client, BBR run
sysctl -w net.ipv4.tcp_congestion_control=bbr
iperf3 -c SERVER_IP -t 30 -R --json > bbr_reverse.jsonReading the results, not just the Mbps number
| Metric | Where it shows up | What it tells you |
|---|---|---|
| Throughput | iperf3 sum bits/sec | Raw goodput — but a clean link shows little difference |
| RTT | –json end.sum_rtt | BBR’s model reacts to RTT; sanity-check path latency |
| Retransmits | TCPRetransmits / ss -ti | Loss on the path is where BBR usually wins |
| Delivery rate | ss -ti ‘bw =’ | Per-socket estimate of achieved rate |
If your link has effectively zero loss, expect CUBIC and BBR within a few percent — the change is not worth the risk of an untested qdisc. If you see retransmits on the path (common on long-haul or congested uplinks), BBR typically recovers more throughput because it does not interpret loss as a hard capacity signal.
Confirm at the socket level
# watch per-connection congestion state and delivery rate
ss -ti dst SERVER_IP
# look for: bbr cwnd:.. rtt:.. bw:..A BBR socket reports bbr as the algorithm and exposes bw (delivery rate). If you still see cubic, your sysctl was not applied to new sockets — existing connections keep their original algorithm.
Caveats specific to VPS hosts
- Bufferbloat elsewhere on the path can mask BBR’s benefit; fix the queue first if local (see fq_codel/CAKE).
- BBR is sender-side; you control only your VPS, not the remote peer.
- BBR competes differently with loss-based flows sharing the same bottleneck — do not assume you win every time.
- CPU-sensitive: on a single-vCPU box, high-rate BBR can cost measurable CPU versus CUBIC.
Running the benchmark across real latency
iperf3 on a LAN tells you almost nothing about a WAN path. To make the test meaningful, inject latency and loss so the difference between algorithms has room to show. tc netem on a test interface lets you reproduce a realistic path deterministically:
# on a test host, add 100ms RTT and 1% loss outbound
sudo tc qdisc add dev eth0 root netem delay 50ms loss 1%
# now repeat the CUBIC and BBR iperf3 runs
sudo tc qdisc del dev eth0 root # remove when doneWith 50ms one-way delay and 1% loss, CUBIC typically backs off hard on each loss event while BBR holds throughput closer to the path’s capacity. That is the scenario where the change pays for itself — and the scenario a clean LAN test would never reveal.
Turning results into a decision
| Your path looks like | Expected BBR gain | Recommendation |
|---|---|---|
| Clean LAN / loopback | ~0% | Leave CUBIC; no benefit, added complexity |
| Low RTT, no loss | <5% | Not worth changing on a production box |
| High RTT + loss | Often 20–200% | Enable BBR + fq and re-measure |
| Shared bottleneck, many flows | Variable | Test under realistic contention before committing |
Roll it out the way you would any other kernel tuning: on one host, measured before and after, with a rollback command ready. To revert, set net.ipv4.tcp_congestion_control=cubic and remove the default_qdisc line, then confirm new sockets report the old algorithm.
Change one dial at a time, measure the same path before and after, and keep a note of the kernel version — BBR’s behaviour has evolved. For a workload where every millisecond of TTFB counts, combine this with latency reduction work at the edge rather than tuning the transport in isolation — the TTFB and edge-caching approach stacks well with a tuned transport.
On a modest cloud instance, this is one of the few kernel changes with a large, repeatable upside on real-world traffic, provided you validate it on a path that actually has loss. Skip the validation and you are just hoping.


Leave a Reply
You must be logged in to post a comment.