BBR (Bottleneck Bandwidth and RTT)
A congestion control algorithm from Google that measures a path's bottleneck bandwidth and minimum round-trip time, then paces data at that modelled rate instead of raising the rate until packets drop. Sender-side only, implemented for TCP and QUIC. It keeps queues shallow rather than full.
Also known as TCP BBR, Bottleneck Bandwidth and Round-trip propagation time.
Full Explanation
BBR stands for Bottleneck Bandwidth and Round-trip propagation time. In the acronym, RTT means the round-trip propagation time. That is the path's minimum delay with queues empty. BBR is a congestion control algorithm developed at Google. It measures two physical properties of the path a connection takes: its bottleneck bandwidth and its minimum round-trip time. It builds an explicit model from them. Then it paces data into the network at that modelled rate. BBR is the alternative to loss-based control. CUBIC (RFC 9438) grows the congestion window until a packet drops. Then it backs off. BBR works differently. It aims at the rate the path can actually carry. It leaves the queue nearly empty (Cardwell et al., ACM Queue, 2016).
BBR is not a caching or CDN feature. It is not a change to the wire protocol. It “runs purely on the sender and does not require changes to the protocol, receiver, or network, making it incrementally deployable”. It depends only on RTT and packet-delivery acknowledgment. So it can be implemented for most transports. That is why an edge server operator can switch to it unilaterally. They can switch one server at a time, with no client change. Open-source implementations exist for TCP and QUIC (draft-ietf-ccwg-bbr-06, abstract). Version matters more than with most algorithms. Mainline Linux's bbr module is still BBRv1. The IETF CCWG specification and Google's own deployment are BBRv3. The two differ in how they treat loss, ECN and the minimum-RTT probe.
How it works
Every acknowledgement gives BBR a round-trip time sample and a delivery-rate sample. Delivery rate is “the ratio of data delivered to time elapsed: deliveryRate = Δdelivered/Δt”. Two filters over those samples make up the whole model (ACM Queue, 2016):
- Bottleneck bandwidth (BtlBw) is the highest rate the slowest link on the path can deliver. BBR estimates it as the maximum recent delivery rate. Linux's tcp_bbr takes a windowed maximum over the last 10 round trips (tcp_bbr.c).
- Round-trip propagation time (RTprop) is the path's minimum RTT with queues empty. BBR estimates it as a running minimum. The paper describes the window as “typically tens of seconds to minutes”. Both Linux (bbr_min_rtt_win_sec = 10) and the IETF specification (BBR.MinRTTFilterLen) use 10 seconds.
The model gives one target: “A connection runs with the highest throughput and lowest delay when (rate balance) the bottleneck packet arrival rate equals BtlBw, and (full pipe) the total data in flight is equal to the BDP (= BtlBw × RTprop)”. So BBR paces each packet to arrive at the bottleneck at BtlBw: “pacing_rate is BBR's primary control parameter”. BBR also holds roughly one bandwidth-delay product in flight. Pacing needs a scheduler: “In Linux, sending uses the efficient FQ/pacing queuing discipline”.
BBR cannot measure the two quantities at the same time. That is because “the pipe has to be overfilled to find its capacity, which creates a queue that obscures the length of the pipe”. So BBR cycles through four states. Each state samples one property at a time:
- Startup is “a binary search for BtlBw by using a gain of 2/ln2 to double the sending rate while delivery rate is increasing”. That finds BtlBw in about log₂(BDP) round trips. But it “creates up to 2BDP excess queue in the process”.
- Drain “uses the inverse of Startup's gain to get rid of the excess queue, then to ProbeBW once the inflight drops to a BDP”.
- ProbeBW is the steady state. BBR was designed to spend about 98 percent of its time here. It “periodically spends an RTprop interval at a pacing_gain > 1, which increases the sending rate and inflight”. Then it removes that queue “by sending at a compensating pacing_gain < 1 for the next RTprop”. The average gain across the cycle is 1.0. So BBR discovers new capacity without leaving a standing queue.
- ProbeRTT is a deliberate, brief emptying of the queue to refresh RTprop. BBRv1 is the version in mainline Linux. In BBRv1, if the RTprop estimate has not been refreshed for more than 10 seconds, BBR caps the window at four packets (bbr_cwnd_min_target = 4). It holds that cap for at least 200 ms and one round trip. BBRv3 is gentler. It reduces in-flight data to 50 percent of the estimated BDP for 200 ms. It does this no more often than once every 5 seconds (draft-ietf-ccwg-bbr-06, section 2.16.2). Large flows drain many packets when they do this. So several flows see a new minimum RTT together. That is how BBR flows converge on a fair share.
Loss is where the versions part company. BBRv1 does not use loss as its primary congestion signal. It has no explicit loss-rate target. Its core “does not react directly to packet losses or delays, although BBR may adjust the size of next send per ACK when loss is observed, or adjust the sending rate if it estimates there is a traffic policer, in order to keep the drop rate reasonable” (tcp_bbr.c). BBRv2 added ECN and loss as model signals. BBRv3 is “BBRv2 + bug fixes and performance tuning” (IETF 119, Google). It carries an explicit per-round-trip loss ceiling of BBR.LossThresh = 2 percent and shallow-threshold ECN.
Why it matters for a CDN
The connection from a CDN edge to an end user is the exact path BBR was designed for. It is a last-mile link that is either deeply buffered or mildly lossy, and often both. Loss-based control handles neither well. “When bottleneck buffers are large, loss-based congestion control keeps them full, causing bufferbloat. When bottleneck buffers are small, loss-based congestion control misinterprets loss as a signal of congestion, leading to low throughput” (ACM Queue, 2016). The first case inflates latency for everything sharing the link. The second destroys throughput.
The measured gap is large. Picture a 100 Mbps/100 ms link with random loss. CUBIC's throughput “decreases by 10 times at 0.1 percent loss and totally stalls above 1 percent”. BBR reaches the theoretical ceiling of link rate × (1 − lossRate) “up to a 5 percent loss and is close up to 15 percent”. Mobile and congested residential access sits squarely in that range.
Google's own edge deployment shows what to expect and what not to expect. Google measured more than 200 million YouTube playback connections on five continents. BBR “reduces median RTT by 53 percent on average globally and by more than 80 percent in the developing world”. Throughput barely moved. BBR “only slightly improves connection throughput because YouTube already adapts the server's streaming rate to well below BtlBw to minimize bufferbloat and rebuffer events”. The lesson for a CDN is that BBR's reliable win is queueing delay. That means start-up time and responsiveness improve, not raw bitrate. This matters especially where adaptive bitrate already caps the sending rate. Where the bottleneck really is loss on shallow-buffered links, the throughput win can be enormous. On Google's B4 WAN, BBR's throughput was “consistently 2 to 25 times greater than CUBIC's”.
What CDNs do
- Cloudflare has “been using [BBR] for much of our traffic”. It is now testing successors on top of it. As of September 2025 it is “running our experiments and improved algorithms for congestion control on all of our free tier QUIC traffic”. It reports “as much as a 10% improvement as compared to the baseline”. It plans to extend to TCP and to all customers “over 2026 and beyond” (Cloudflare blog).
- Amazon CloudFront switched to BBR as its TCP congestion control “During March and April 2019”. It reported “performance gains of up to 22% improvement on aggregate throughput across several networks and regions”. AWS qualifies that figure. The gains “depend on a number of factors, including the resource size as well as the quality, capacity, and distance of the connectivity in a given last-mile network” (AWS blog).
- Fastly makes it a per-connection, opt-in choice rather than a platform default. The VCL variable client.socket.congestion_algorithm takes “cubic, reno, or bbr, with cubic being the default”. Fastly's own documentation still describes BBR as “the newest of these algorithms” and “still evolving and considered experimental”. VCL that sets the variable should read it back, because the kernel may not honour the selection (Fastly docs).
- Akamai completed a global rollout of its own BBR variant, tuned for the Akamai network, in November 2019. It is one of about five congestion control algorithms Akamai runs. A machine-learning model selects the algorithm per end-user connection, rather than pinning one per customer. On announcement, BBR was “serving more than 80% of end user connections on the Adaptive Media Delivery and Download Delivery products” (StreamTV Insider, December 2019). The reported benefit was specific, not blanket. For adaptive media delivery, “goodput improved by nearly 19% for files larger than 1 MB”. One multi-CDN customer saw “anywhere from 5% to 18% globally” (Streaming Media, December 2019). Treat all four of these as dated vendor statements. None of the four publishes a current per-POP algorithm inventory.
Watch out for
- BBRv1 takes more than its share next to CUBIC. “BBR v1 is known to compete unfairly with CUBIC, HTCP, etc, so we do not recommend it on networks that use other types of congestion control” (ESnet). Better coexistence with Reno and CUBIC is one of the stated reasons Google wants to replace v1 with v3 in place (IETF 119). If your edge shares a bottleneck with loss-based traffic you do not control, this is the main risk.
- What you enable is not what is specified. Turning on bbr on a mainline kernel gives you BBRv1. Google marks BBRv1 obsolete and deprecated. BBRv3 “was released in July, 2023” (ESnet). But it ships as an out-of-tree Google branch “intended to enable research collaboration and wider testing” (google/bbr v3 README). The IETF document that specifies it is draft-ietf-ccwg-bbr-06. It is a CCWG working-group draft with intended status Experimental. BBRv2 never reached a supported kernel release. So we run BBR is an ambiguous statement. Any paper or benchmark you rely on must name its version.
- Token-bucket policers defeat the model. BBR's own YouTube deployment “revealed that most of the world's ISPs mangle traffic with token-bucket policers”. The bucket is full at connection start. So BBR learns the unpoliced BtlBw. Once the bucket empties, everything above the much lower fill rate is dropped. BBR relearns. But the ProbeBW cycle keeps re-probing above the policed rate. Expect elevated loss on policed access networks, not a clean rate match.
- A small receive buffer will cap BBR before the network does. On Google's B4 WAN, “75 percent of BBR connections were limited by the kernel's TCP receive buffer”. Operators had deliberately set that buffer to 8 MB to stop CUBIC flooding the network. Raising it on one path between the US and Europe took BBR from that ceiling to 2 Gbps. CUBIC stayed at 15 Mbps on the same path.
- The kernel sysctl does not touch HTTP/3. QUIC's congestion controller is part of the QUIC sender, not the kernel TCP stack. RFC 9002 specifies a NewReno-like controller. It notes “A sender can unilaterally choose a different algorithm to use” (RFC 9002 section 7). Google's QUIC BBR lives in the userspace quiche code (BBR FAQ). On a mixed edge you must configure TCP and QUIC separately. They may not be running the same version of BBR.
- Pacing still wants a suitable qdisc. Since Linux 4.20, “there is no longer a strict requirement to install the ‘fq’ qdisc to use BBR. Any qdisc will work, though ‘fq’ performs better for highly-loaded servers” (Google BBR quick-start).
- It is not a substitute for fixing the network. “BBR may reduce the impact of packet loss, but does not eliminate it”. Improvements like it are “not a substitute for good network design … or the need to reduce packet loss to a minimum” (ESnet).
Best practice
- Enable it with the pacing scheduler on Linux edge servers. Set net.ipv4.tcp_congestion_control=bbr and net.core.default_qdisc=fq. Persist these under /etc/sysctl.d/. BBR has been in Linux since kernel 4.9. So RHEL/Rocky 8, Debian 9 and Ubuntu 17 onwards already have the module (ESnet).
- Confirm what is actually running rather than what is configured. sysctl net.ipv4.tcp_congestion_control reports the setting. ss -tin shows per-connection BBR state: “pacing rate, cwnd, bandwidth estimate, min_rtt estimate” (BBR FAQ). If the bbr:(bw:…,mrtt:…) line is missing, BBR is not driving that connection.
- Raise the TCP receive buffer to at least the path bandwidth-delay product before concluding BBR did not help. A buffer sized to restrain CUBIC will silently cap BBR.
- A/B test per route, ASN or POP rather than flipping the platform. Both the CloudFront and Akamai results are qualified by object size and last-mile conditions. ESnet's testing shows the same: “BBR can help a lot on certain links”. That is not the same as all of them.
- Decide the version deliberately. If you share bottlenecks with CUBIC or Reno traffic, BBRv1 is the wrong default. If you want BBRv3, you are running an out-of-tree kernel. So heed ESnet's advice: “Those interested in BBRv3 should do extensive testing in their environment, and follow the discussion on the BBR discussion group list before deploying it on production systems”.
- Configure the QUIC stack separately. Record which version of BBR each leg runs. Then a performance regression can be attributed to a specific version, rather than to BBR in the abstract.
Examples
# Check current congestion control algorithm on Linux
sysctl net.ipv4.tcp_congestion_control
# net.ipv4.tcp_congestion_control = cubic
# Enable BBR
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
sudo sysctl -w net.core.default_qdisc=fq
# Make persistent across reboots
echo 'net.ipv4.tcp_congestion_control=bbr' | \
sudo tee -a /etc/sysctl.d/99-bbr.conf
echo 'net.core.default_qdisc=fq' | \
sudo tee -a /etc/sysctl.d/99-bbr.conf
sudo sysctl -p /etc/sysctl.d/99-bbr.conf
# Verify BBR is loaded
sysctl net.ipv4.tcp_available_congestion_control
# reno cubic bbr
# Check BBR state for active connections
ss -tin | grep bbr
# Shows BBR-specific info: bw, mrtt, pacing_rate
# Nginx: BBR applies at the OS level, no app config needed
# Just enable BBR on the server running your CDN edge
Frequently Asked Questions
A congestion control algorithm from Google that measures a path's bottleneck bandwidth and minimum round-trip time, then paces data at that modelled rate instead of raising the rate until packets drop. Sender-side only, implemented for TCP and QUIC. It keeps queues shallow rather than full.
# Check current congestion control algorithm on Linux
sysctl net.ipv4.tcp_congestion_control
# net.ipv4.tcp_congestion_control = cubic
# Enable BBR
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
sudo sysctl -w net.core.default_qdisc=fq
# Make persistent across reboots
echo 'net.ipv4.tcp_congestion_control=bbr' | \
sudo tee -a /etc/sysctl.d/99-bbr.conf
echo 'net.core.default_qdisc=fq' | \
sudo tee -a /etc/sysctl.d/99-bbr.conf
sudo sysctl -p /etc/sysctl.d/99-bbr.conf
# Verify BBR is loaded
sysctl net.ipv4.tcp_available_congestion_control
# reno cubic bbr
# Check BBR state for active connections
ss -tin | grep bbr
# Shows BBR-specific info: bw, mrtt, pacing_rate
# Nginx: BBR applies at the OS level, no app config needed
# Just enable BBR on the server running your CDN edge
Yes. BBR (Bottleneck Bandwidth and RTT) is also known as TCP BBR, Bottleneck Bandwidth and Round-trip propagation time. A congestion control algorithm from Google that measures a path's bottleneck bandwidth and minimum round-trip time, then paces data at that modelled rate instead of raising the rate until packets drop. Sender-side only, implemented for TCP and QUIC. It keeps queues shallow rather than full.
Related CDN concepts include:
- Latency — Latency is the time data takes to travel from one point on a network to …
- TCP (TCP) — TCP is the connection-oriented transport specified in RFC 9293: it delivers application data as one …
- Throughput — Throughput is the rate at which data actually crosses a link in a measured interval …
- CWND (Congestion Window) (CWND) — The congestion window (cwnd) is the TCP sender’s own limit on how much unacknowledged data …