Introduction

Every time you load a video or push a file to the cloud, your device and the server negotiate a silent question: how fast can I send without crashing the network?

The traditional answer — used by TCP for decades — is: send faster and faster until something breaks, then back off. A dropped packet is a signal to slow down. It works, but it has a hidden flaw: by the time packets are dropping, the buffers between you and the server are already full. That full buffer is the cause of the sluggish, jittery internet we all know.

In 2016, Google engineers Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, and Van Jacobson published BBR (Bottleneck Bandwidth and Round-trip propagation time). Instead of waiting for loss, BBR asks a different question: what is the actual speed of the slowest link on the path, and how long does a packet actually take to travel with an empty pipe? Answer those two, and you can fill the pipe exactly — no buffer bloat, no unnecessary pauses.

BBR is already deployed on Google's backbone, YouTube, and QUIC, the transport behind much of today's HTTP/3 web. Understanding it means understanding one of the most important ideas in modern networking: model the network, don't just react to it.

Try It

The simulation below models a single bottleneck link. You control the link's capacity and the propagation delay. Run BBR to see how it probes, estimates, and then locks into the right sending rate. Run Loss-Based to compare the classic approach.

<!-- {{c_html_comment}} -->
<div class="controls">
  <label>{{lbl_bw}} <input type="range" id="bw" min="1" max="20" value="10"> <span id="bw-val">10</span> Mbps</label>
  <label>{{lbl_rtt}} <input type="range" id="rtt" min="10" max="200" value="50"> <span id="rtt-val">50</span> ms</label>
</div>
<div class="btn-row">
  <button id="btn-bbr" type="button">{{btn_run_bbr}}</button>
  <button id="btn-loss" type="button" class="secondary">{{btn_run_loss}}</button>
  <button id="btn-reset" type="button" class="ghost">{{btn_reset}}</button>
</div>
<canvas id="chart" width="560" height="220"></canvas>
<div class="legend">
  <span class="dot bbr"></span> BBR &nbsp;
  <span class="dot loss"></span> {{lbl_loss_based}} &nbsp;
  <span class="dot bdp"></span> BDP
</div>
<div id="info" class="info">{{info_idle}}</div>
/* {{c_css_comment}} */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; color: #222; }
.controls { display: flex; flex-wrap: wrap; gap: .5rem 1.2rem; margin-bottom: .6rem; font-size: .85rem; }
label { display: flex; align-items: center; gap: .35rem; }
input[type=range] { width: 110px; cursor: pointer; }
.btn-row { display: flex; gap: .5rem; margin-bottom: .7rem; flex-wrap: wrap; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border-radius: 7px; cursor: pointer; border: 1px solid #1d4e7a; }
button:not(.secondary):not(.ghost) { background: #1d4e7a; color: #fff; }
button.secondary { background: #c0392b; color: #fff; border-color: #c0392b; }
button.ghost { background: #fff; color: #333; border-color: #aaa; }
canvas { display: block; max-width: 100%; border: 1px solid #dde3ea; border-radius: 8px; background: #fafcfd; }
.legend { font-size: .78rem; margin-top: .35rem; display: flex; align-items: center; gap: .25rem; color: #555; }
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
.dot.bbr { background: #1d4e7a; }
.dot.loss { background: #c0392b; }
.dot.bdp { background: #2ecc71; border: 2px solid #27ae60; background: transparent; border-radius: 0; height: 2px; width: 18px; }
.info { font-size: .85rem; margin-top: .45rem; min-height: 1.5em; color: #444; }
.info.ok { color: #0a7d33; font-weight: 600; }
.info.warn { color: #b7700a; font-weight: 600; }
// Code not found

Notice how BBR reaches full throughput quickly and keeps the in-flight data near the bandwidth-delay product (BDP) — the ideal pipe fill. The loss-based sender overshoots, fills the buffer, and then oscillates. The difference is not raw speed but where in the system the data piles up.

The Real Complexity

BBR is not one algorithm but a state machine with four phases:

  • Startup: BBR doubles the sending rate each round (like TCP slow start) until it detects that bandwidth is no longer growing. This fills the pipe in O(log2BDP)O(\log_2 BDP) round trips.
  • Drain: after startup, the buffer has been over-filled slightly. BBR slashes the rate to drain it, then transitions to steady state.
  • ProbeBW: the steady-state phase. For most of the time BBR sends at its estimated bottleneck rate, but every 8 rounds it probes slightly higher to detect if more bandwidth has opened up. If the RTT stays flat during the probe, the extra bandwidth is real.
  • ProbeRTT: periodically BBR drops its inflight window to a minimum for 200 ms. This drains any queue and lets it re-measure the true propagation delay RTTminRTT_{min}.

The key insight is the bandwidth-delay product: the ideal amount of data in flight is BDP=BtlBw×RTTpropBDP = BtlBw \times RTT_{prop}, where BtlBwBtlBw is the bottleneck bandwidth and RTTpropRTT_{prop} is the minimum observed RTT. BBR never sends more than 2×BDP2 \times BDP, keeping buffers nearly empty.

What remains open? BBR's fairness between multiple competing flows is still debated. In practice, BBR flows can be aggressive toward loss-based flows sharing the same bottleneck. BBR v2 (published 2019) adds explicit loss signals to improve coexistence, and research continues. Unlike most algorithmic problems on this site, BBR's correctness is not a theorem — it is an empirical claim continuously tested at internet scale.

Where It Matters

BBR's model-based approach pays off most in conditions where loss-based TCP struggles:

  • Long-fat networks: a trans-Pacific path with 150 ms RTT and 10 Gbps capacity has a BDP of 187\approx 187 MB. Loss-based TCP can never keep that pipe full; BBR was designed for exactly this case.
  • Lossy wireless links: satellite and mobile networks drop packets due to noise, not congestion. Loss-based TCP interprets every drop as a sign to slow down; BBR ignores noise-induced loss if the RTT stays low.
  • YouTube and Google backbone: Google reported 4% higher throughput globally and 14% more throughput for the slowest connections after switching to BBR internally.
  • QUIC / HTTP/3: QUIC implements its own congestion control in user space, making it easy to ship BBR everywhere without waiting for OS updates.
  • Linux kernel: BBR has been available as a selectable TCP congestion algorithm in Linux since kernel 4.9 (2016). Many CDN and cloud providers now default to it.

The broader lesson mirrors what information theory taught about communication channels: to get the best throughput, you must measure the channel rather than guess at it.

Conclusion

For thirty years, congestion control meant: send until you break something, then apologize. BBR changed the question. Instead of reacting to pain, it measures the network's true capacity — the bottleneck bandwidth and the propagation delay — and uses those two numbers to fill the pipe just right.

The result is faster video, smoother calls, and more responsive downloads, especially over the long or lossy paths that make up much of the real internet. The engineering lesson generalizes: whenever a system oscillates because it only knows how to react, ask whether you can instead model the system and act on that model directly.

BBR is a deployed answer to that question, and it is still being refined. If you want to see the same spirit applied at the information-theoretic limit, look at how information theory defines channel capacity — the deepest reason any congestion control algorithm must eventually measure and not just react.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/bbr-congestion/Content licensed under CC BY-NC 4.0.