Tuning TCP Congestion Control on a High-Latency VPS: CUBIC vs BBR, Verified

On a WAN link with 100 ms or more of round-trip delay, a small VPS running the default CUBIC congestion control spends most of its time growing its window slowly and then backing off hard after any loss. Switching or tuning the congestion algorithm changes throughput measurably — but only if you understand what the kernel is doing and verify the change took effect. This is a hands-on procedure, not a magic sysctl.

Confirm What Is Actually Loaded

Check available algorithms and the current selection before changing anything:

sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_congestion_control
lsmod | grep -E 'tcp_bbr|tcp_htcp'

If bbr does not appear in the available list, load the module first. On kernels 4.9+ it ships in-tree; on some minimal VPS images it is a module you must load explicitly:

sudo modprobe tcp_bbr
echo "tcp_bbr" | sudo tee /etc/modules-load.d/bbr.conf
sysctl net.ipv4.tcp_available_congestion_control

When to Change the Algorithm

The right choice depends on the path, not on folklore. CUBIC is loss-based: it assumes packet loss means congestion and cuts its window accordingly. On a lossy but uncongested long-haul link — common between continents — that assumption is wrong and throughput collapses. BBR is model-based: it estimates bottleneck bandwidth and minimum RTT directly, and tolerates random loss far better.

ScenarioLoss-based (CUBIC)Model-based (BBR)
LAN / low-latencyExcellentComparable
WAN >100 ms, low lossUnderutilizes linkFills link better
Lossy mobile / satelliteThrottles hardSubstantially higher throughput
Fairness with many flowsWell-studiedCan be aggressive

The tradeoff is fairness and buffer pressure. BBR can be more aggressive toward competing flows and, on shallow buffers, push more into the network than CUBIC would. On a VPS acting as a busy public server, test before committing.

Buffer and Queuing Settings That Must Move Together

Changing the congestion control without a sane queue discipline is half a fix. Enable the fair-queuing or CAKE discipline so a single bulk transfer cannot starve latency-sensitive traffic, and set the send buffer to match the bandwidth-delay product:

# fq is the standard companion to BBR
sysctl -w net.core.default_qdisc=fq

# BDP example: 100 Mbit/s x 150 ms = ~1.9 MB
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456"
sysctl -w net.ipv4.tcp_wmem="4096 65536 6291456"

Persist these in /etc/sysctl.d/99-bbr.conf so they survive reboot. For a wider walkthrough of related knobs, the earlier guide on this site covers socket memory and buffer sizing in more depth.

Verify with tcp_info, Not Vibes

The kernel exposes per-socket diagnostics through ss. Watch the delivery rate and retransmit counters during a real transfer:

ss -tin state established '( dport = :443 or sport = :443 )'
# Watch the 'bbr' section: bw = delivery rate estimate
watch -n1 "ss -tin | grep -A1 bbr"

Then run a controlled test with iperf3 over a high-latency path and compare both algorithms using the same parameters and the same time window. Record retransmits as well as throughput — a throughput win that doubles retransmits is not a win on a metered or shared link.

  • Run each algorithm for at least 60 seconds, three times.
  • Compare mean throughput and retransmit ratio.
  • Test with concurrent flows, not a single stream, if your workload is web traffic.
  • Re-test at different times of day to rule out transient path conditions.

Rollback and Monitoring

Keep the change reversible and observable. A one-line rollback to CUBIC should always be available, and you should track whether the change helped over days rather than seconds:

node_netstat_Tcp_RetransSegs  # via node_exporter
node_netstat_Tcp_OutSegs

A retransmit ratio above roughly 1–2% on a stable path suggests the link itself is the problem, not the algorithm. On paths that are genuinely lossy, no congestion control will make a bad line good — at that point, choosing a provider with a better network path does more than any kernel setting.

Watching the Path Over Time

Congestion control interacts with conditions that change hour to hour. Peak-hour congestion, transit route changes, and even regional weather affecting satellite paths all shift the optimum. A setting that wins at 03:00 may lose at 18:00. Rather than flipping algorithms constantly, track the delivery rate your workloads actually achieve and set an alarm on regressions.

For a web server, the practical proxy is time-to-first-byte from an external monitor plus the retransmit ratio from the host. If TTFB from distant clients improves after switching to BBR while retransmits stay flat, the change is a genuine win. If TTFB is flat but retransmits climb, you have moved the problem rather than fixed it, and the honest answer may be that the path, not the algorithm, is limiting you.

Checklist

  1. Confirm BBR is loaded and available.
  2. Enable fq as the queue discipline.
  3. Size buffers to the measured bandwidth-delay product.
  4. Persist settings in sysctl.d.
  5. Verify with ss -tin and repeatable iperf3 runs.
  6. Monitor retransmits for days before declaring success.

Congestion control is one of the few kernel-level changes with an immediately measurable effect on WAN performance. Change it deliberately, measure honestly, and keep the rollback handy.

Leave a Reply