Computer networks · 23 September 2026 · 25 minutes with the experiments

BBRHow fast is the
path, really?

A sender can fill a link, then keep sending until it fills the queue behind it. Only the first achievement makes the transfer faster. BBR is an attempt to tell those two events apart.

BandwidthDelayThe cost of finding out

Suppose you are sending a large file. Your packets leave the machine; acknowledgments come back. You do not see the routers, their queues, the other senders, or the link that limits your transfer. Yet you have to choose a sending rate. Too little wastes capacity. Too much makes packets wait, and eventually disappear.

The obvious experiment is to send more until something complains. That is a workable starting point. But what counts as a complaint—and how late does it arrive?

Reno’s familiar answer is packet loss: grow the congestion window, then cut it after congestion is inferred. In congestion avoidance, its increase is roughly one segment per round trip; its usual fast-recovery response roughly halves the flight allowance. This gives senders a way to explore and share capacity using sparse feedback. [1] On a path with a deep tail-drop buffer, however, the first overflow can arrive long after the link became fully busy. The intervening experiment discovered how much waiting room the router had.

BBR asks an earlier question: at what point does putting more data into the path stop increasing the rate at which data comes out? To see why that question changes the design, we need to separate a rate from an amount.

The path through the argument
  1. Enough data to keep the link busy
  2. Why matching the rate does not drain the queue
  3. How the sender measures an invisible path
  4. Why it must disturb what it measures
  5. Why the four BBR states exist
  6. What Reno, Copa, and CoDel disagree about
  7. The model’s blind spots, and BBRv3

01 / The operating point

How much data is enough?

Give the path a bottleneck rate of 10 MB/s and an unloaded round-trip time of 50 ms. MB means a million bytes. Assume one continuously backlogged sender, smooth pacing, a stable path, immediate acknowledgments, no losses, and no meaningful reverse-path queue. Treat packet sizes as small enough to ignore. These assumptions let us inspect the accounting before confronting the Internet.

A byte that you send remains unacknowledged for 50 ms even when nothing is congested. To keep sending at 10 MB/s while the first acknowledgments are still on their way back, you need permission to have this much data outstanding:

Bandwidth-delay product10 MB/s × 0.050 s = 0.50 MB

This is the bandwidth-delay product, or BDP. It is useful to call it the pipe’s size, provided we remember that “in flight” means sent but not yet acknowledged. Some of those bytes may already have reached the receiver; their acknowledgments are still returning. BDP is an accounting requirement for keeping the bottleneck busy, not a claim that half a megabyte must physically sit inside a wire.

Now limit outstanding data to 0.25 MB. The sender sends it, runs out of permission, and waits for acknowledgments. Averaged over time, it can deliver only 0.25 / 0.050 = 5 MB/s. The link could carry twice that much. Increase the flight allowance to 0.50 MB and the sender can sustain 10 MB/s.

What if we go to 0.75 MB? The bottleneck cannot process 15 MB/s just because we gave it more work. In the settled, fully used-window model, the extra 0.25 MB waits in the queue. At 10 MB/s, that queue adds 25 ms. Throughput stays at 10 MB/s; RTT becomes 75 ms.

One path, three settled operating points
Actual data in flightDelivery rateQueueRTT
0.25 MB5 MB/s050 ms
0.50 MB10 MB/s050 ms
0.75 MB10 MB/s0.25 MB75 ms

There is a knee in this relationship. Before 0.50 MB, extra outstanding data buys throughput. After 0.50 MB, it buys delay. In symbols, with bottleneck capacity C, unloaded RTT R₀, and actual steady flight size W:

delivery = min(C, W / R₀)queue = max(0, W − C R₀)RTT = R₀ + queue / C

Move through those operating points below. The two charts share the same horizontal axis. Watch exactly where the throughput curve stops climbing and the delay curve starts.

Experiment 1 / settled states

Beyond the knee, only waiting increases

Delivered10 MB/s
Queued0.25 MB
Round trip75 ms

The extra 0.25 MB adds 25 ms of waiting and no throughput.

A steady-state calculation, not a time simulation. One smooth flow; no competing traffic, finite-buffer drops, or ACK irregularity. “Actual data in flight” is distinct from an unused congestion-window limit.

Doesn’t delay explode near 100% utilization?

It does in familiar queueing models with random arrivals and service times, such as M/M/1. Our path is different: one sender controls smooth arrivals to match fixed service. Exact matching can keep a deterministic server busy without a standing queue. Introduce bursts, variable service, competing flows, or delayed feedback, and that exact balance becomes fragile. Some queued work can cushion a temporary gap; too much becomes persistent delay. BBR’s ideal knee is a reference point for control, not a promise of zero queueing on a stochastic network—or a bound on tail latency.

This calculation explains BBR’s ambition: estimate the height of the throughput plateau and the lowest attainable RTT, then operate near their product. It does not yet give us an algorithm. We have assumed the very quantities the sender needs to discover.

02 / Rate and inventory

A correct rate can preserve a bad queue

Stay with the 0.75 MB case. There is 0.25 MB queued. Suppose the sender now paces at exactly 10 MB/s. Does the queue disappear?

No. Ten megabytes per second arrive; ten leave. Every departing byte is replaced. The queue remains 0.25 MB and keeps adding 25 ms to each round trip.

While the bottleneck queue is nonemptyqueue growth = arrival rate − service rate

To remove the queue, arrivals must temporarily fall below service. Send at 7.5 MB/s and the queue drains at 2.5 MB/s. Clearing 0.25 MB takes 100 ms. During that interval, the bottleneck can still deliver 10 MB/s: the missing 2.5 MB/s comes from work already waiting. Only if we continue after the queue empties do we start wasting link capacity.

This is why BBR controls two different things. Pacing schedules how quickly packets leave. The congestion window, or cwnd, limits how much unacknowledged data may exist. The pacer can say “it is time for another packet” while the window says “you already have enough outstanding.” Both constraints matter.

It also resolves an apparent contradiction in BBRv1. Its nominal steady-state window target is about 2 × estimated BDP, with implementation allowances for packetization and ACK aggregation. That does not command the sender to keep two BDPs outstanding. At 10 MB/s with a 50 ms RTT, smooth pacing naturally produces roughly 0.50 MB in flight even when the ceiling is 1 MB. The slack tolerates irregular acknowledgments and allows probing. If a persistent queue lengthens the RTT, however, more of that ceiling can be used. A generous ceiling is useful and potentially costly. [2]

So “BBR sends one BDP” is an intuition about its intended operating region, not a literal description of its window or of every moment in its operation.

03 / Inference from acknowledgments

The sender has no speedometer at the bottleneck

Imagine recording that 0.50 MB was delivered over 50 ms. That implies 10 MB/s of delivery. But counting the bytes covered by the latest ACK and dividing by the gap since the previous ACK is fragile. Routers and receivers can delay ACKs, then release several together. A fast burst of arriving acknowledgments need not represent a fast burst of delivered data.

Linux therefore carries delivery counters and timestamps with sent packets. When a packet is acknowledged, the sender can compare its saved snapshot with the current cumulative delivery count. It estimates a rate over a corresponding interval of sending and acknowledgment activity. In simplified form:

sample = newly accounted delivered data÷ max(send interval, ACK interval)

The intervals here come from the tracked delivery sample; they are not simply the packet’s RTT. Using the larger interval prevents compressed ACKs from turning a slowly sent flight into an arbitrarily large rate estimate. It reduces a measurement error; it does not create an oracle. [4]

Now suppose the application produces only 2 MB/s. Observing 2 MB/s says what was delivered, but says little about whether the network could have delivered ten. BBR marks such samples as application-limited. A low application-limited sample should not drag its bandwidth estimate down; a high one can still demonstrate that a higher rate was achievable. This distinction is why a congestion controller must know something about its own supply of work. [2]

Why keep a maximum of one measurement and a minimum of the other?

Consider recent credible delivery samples of 6, 8, 10, 7, and 9 MB/s. An average says 8. A maximum preserves the evidence that the path recently delivered 10. If the low samples came from temporary underfilling or competition, smoothing them all together would answer a different question from “what rate has proved achievable?”

For RTT, the contamination runs the other way. Queueing adds time. Given samples of 50, 54, 75, 62, and 51 ms on an unchanged path, the minimum of 50 ms is the best candidate here for the unloaded round trip. Averaging produces 58.4 ms and builds some waiting into the supposed baseline.

BBRv1 accordingly tracks a recent maximum delivery rate and a recent minimum RTT—nominally over ten packet-timed rounds and ten seconds, respectively. These are different clocks and different questions. The estimates are often called BtlBw and RTprop. [2]

There is a catch hiding inside both choices. A maximum can preserve an inflated sample or a capacity that no longer exists. A minimum can preserve a route’s obsolete baseline, or remain too high because the queue never empties. “Take the extreme” works only if credible revealing samples arrive often enough and old evidence eventually expires.

Which raises the next problem: what if our own sending behavior prevents those revealing samples from ever occurring?

04 / Measurement changes the system

You have to ask the path a question

Suppose BBR has learned 10 MB/s and 50 ms. It paces at 10 MB/s forever. Tomorrow, competing traffic leaves and 15 MB/s becomes available. What does it observe?

Ten MB/s going out. Ten MB/s coming back. Fifty-millisecond round trips. A perfectly healthy path that could carry more is observationally identical to one whose limit is still ten. A sender cannot discover unused capacity by refusing to use it.

So it briefly sends faster. Consider a 25% increase: 12.5 MB/s for 50 ms. The sender offers 0.625 MB. If capacity is still 10 MB/s, only 0.50 MB can leave the bottleneck during that interval; 0.125 MB accumulates. That adds 12.5 ms of queueing delay. If capacity has grown to 15 MB/s, all 0.625 MB can leave without a standing queue. The successful experiment establishes at least 12.5 MB/s of attainable delivery; it still has not proved that the limit is 15.

Experiment 2 / the same probe, two possible paths

Did we buy throughput, or waiting?

Capacity during the probe
Offered0.625 MB
Delivered0.500 MB
Extra delay12.5 ms

The extra rate produced 0.125 MB of queue. Delivery stayed at 10 MB/s.

Time is measured where traffic reaches the bottleneck. These are exact fluid-accounting results for an initially empty queue. ACK feedback reaches the sender later. This isolates one probe; it is not a complete BBR simulator.

After an unsuccessful probe, merely returning to 10 MB/s preserves the new queue. We have already done that accounting. A complementary 7.5 MB/s interval drains 0.125 MB in 50 ms. The speed-up and slow-down each perform a specific job: one tests for capacity, the other removes the excess inventory when capacity was unchanged.

Measuring the unloaded RTT creates the opposite demand. A full bottleneck reveals its delivery capacity, but a persistent queue hides its unloaded delay. To see a low RTT, BBR must occasionally reduce outstanding data enough to give queues a chance to empty. That can sacrifice throughput, and other senders may keep the queue busy anyway.

These are incompatible measurement conditions, not contradictory objectives. You want a busy bottleneck to learn its rate and an unqueued path to learn its baseline. BBR alternates between experiments because one passive observation cannot reliably reveal both.

05 / From argument to algorithm

The four states now have a reason to exist

We can reconstruct the original BBR state machine from the questions it needs to answer. The names are less important than the job each state performs.

1

STARTUP

How much more can the path deliver? Raise the sending rate rapidly. In BBRv1, a roughly 2.89 pacing gain helps produce exponential growth. If bandwidth fails to grow by about 25% over three qualifying rounds, infer that the pipe has filled. More offered load without more delivery is the useful observation.

2

DRAIN

How much excess did discovery leave behind? Pace below the estimated delivery rate, using roughly the reciprocal of the startup gain. Let queued data keep the bottleneck busy while outstanding data falls toward the estimated BDP.

3

PROBE_BW

Is the old bandwidth estimate still right? Keep testing while carrying useful traffic. BBRv1’s nominal eight-phase pacing cycle uses gains 1.25, 0.75, then six phases at 1.0. Phase exits depend on time and flight/loss conditions; this is not an exact eight-RTT metronome. Linux also applies a small pacing margin.

4

PROBE_RTT

Is the old minimum RTT still credible? When its minimum-RTT evidence expires, BBRv1 sharply limits flight to four packets. After reaching that small flight size, it waits at least about 200 ms and completes a packet-timed round before leaving. Naturally low RTT samples and idle periods can avoid unnecessary probes.

Those constants describe the cited BBRv1 code, not all versions of BBR. [2] In ordinary operation the flow spends most of its time in PROBE_BW, occasionally visiting PROBE_RTT. STARTUP and DRAIN solve the initial discovery problem.

The nominal control equations are compact:

estimated BDP = estimated bandwidth × minimum RTTpacing rate ≈ pacing gain × estimated bandwidthflight ceiling ≈ cwnd gain × estimated BDP

The state machine changes the gains and constrains the ceiling. ACK processing refreshes the model. Loss recovery, minimum windows, packetization, and ACK aggregation add the practical details. The equations explain what the controller is trying to do; the state machine explains how it tries to learn enough to do it.

06 / The disagreement underneath the algorithms

What should count as evidence of “too much”?

Reno, Copa, and BBR are sender congestion controllers. CoDel lives at a queue and manages that queue. Putting all four in one list is useful only if we keep that distinction visible.

Different observations support different decisions
MechanismObservation and policyWhat it asks us to trust
Reno
Sender
Grow the window; reduce it on inferred congestion, classically loss.Congestion feedback arrives soon enough to guide a useful operating point.
Copa
Sender
Use estimated queueing delay to compute a target rate, then adjust the window toward it.The delay estimate and competition policy make backing off productive.
BBR
Sender
Estimate delivery capacity and baseline RTT; pace and limit flight, with active probing.Recent measurements and controlled probes identify a sustainable rate and flight volume.
CoDel
Queue
Observe local packet sojourn times; signal when excessive delay persists.Persistent local waiting merits intervention, and senders respond to the feedback.

Copa treats queueing delay as a price

Copa’s target rate is 1 / (δ × dq), in packets per second when queueing delay dq is in seconds. With fixed δ, doubling the estimated queueing delay halves that target. Its window update tries to move toward the target; observing a target is not the same as instantly attaining it. Copa also adapts δ when competing with buffer-filling traffic. [5]

BBR gives the same RTT increase a different role. In its original design, a larger current RTT does not directly imply “make the rate inversely smaller.” It retains a baseline-delay estimate and a delivery model, then uses deliberate changes in rate and flight to test them. Two senders can receive identical RTT information and choose different actions because their policies ask different questions of it.

This matters with competing traffic. Suppose another sender maintains a standing queue. A controller that interprets every increase in delay as a reason to surrender rate can yield more and more bandwidth without removing that queue. The other sender replaces what it surrendered. This is why choosing a good solitary operating point is only part of congestion control: the controller must also behave sensibly when other controllers react.

CoDel knows something the sender cannot directly see

A queue can timestamp a packet at enqueue and subtract at dequeue. CoDel uses this local sojourn time to distinguish bursts that clear from excessive waiting that persists. With the usual 5 ms target and 100 ms interval, persistently above-target samples trigger congestion signaling, subject to the small-backlog safeguard. The interval is a duration for judging persistence, not a fixed count of this sender’s RTTs. CoDel can drop packets or, where appropriate, mark them with ECN. [6]

There is a revealing connection between CoDel’s use of a minimum and BBR’s. In CoDel, a sufficiently low recent sojourn sample is evidence that this queue is clearing. In BBR, a low end-to-end RTT sample is a candidate for the path’s unloaded baseline. Both resist mistaking every transient spike for enduring congestion. They observe different objects and use their minima to make different decisions.

CoDel and BBR can therefore coexist: one governs a queue, the other governs a sender. Their interaction still depends on the sender’s response to drops or marks. Installing an AQM does not make that response irrelevant.

07 / Where the argument stops being a guarantee

A good model is still a model

Our first path had one sender and a fixed bottleneck. That let us label 10 MB/s as a physical capacity. With competing flows, the sender’s delivery samples reflect the service it obtained under the current contention and queue scheduling. They do not reveal the link’s nameplate rate, the amount another flow will relinquish, or the sender’s morally correct share.

Imagine two flows that have each recently observed 8 MB/s, now sharing a 10 MB/s link. Each estimate can be an honest record of the past. Together their current intentions are impossible. A filter cannot settle this allocation problem by remembering harder. The controllers need interaction rules that converge to an acceptable division.

Early BBR made this distinction painfully concrete. Measurements of the original algorithm found substantial RTT unfairness in some settings: longer-RTT BBR flows could dominate shorter-RTT flows. One mechanism is visible in our probe accounting. At a fixed fractional excess rate, probing for a longer RTT injects more excess bytes. That is a property of the experiment, not evidence that the longer path deserves more capacity. This is a version-specific result, not a universal ranking of congestion controllers. [7]

Now change our example’s route so that the true unloaded RTT rises from 50 to 70 ms while capacity stays 10 MB/s. A stale 50 ms minimum says the extra 20 ms is queueing. It also leaves the estimated BDP at 0.50 MB when the new value is 0.70 MB. Conversely, if a persistent queue keeps every RTT sample above the true baseline, the model can overestimate the amount needed to fill the path. The problem is not arithmetic. It is identifying which hidden change produced the observation.

Change capacity instead, from 10 to 5 MB/s. A recent maximum of ten is now an instruction to oversend until the model adapts or another bound intervenes. A longer memory survives brief disturbances; it also remembers obsolete conditions longer. A shorter memory adapts faster; it more readily mistakes a temporary lull for lost capacity.

Why BBRv3 is more than the original two estimates

The July 2026 IETF draft specifies BBRv3. It retains model-based pacing but uses loss-informed bounds on bandwidth and flight volume, with more structured bandwidth probing: DOWN, CRUISE, REFILL, and UP. The aim is to explore capacity while containing excess load and leaving room for other traffic. Do not transfer BBRv1’s gain-cycle constants or four-packet PROBE_RTT recipe directly to v3. [3]

“BBR ignores loss” is therefore a bad general definition. Even v1 still lives inside a reliable transport with loss recovery; its main rate model is not Reno’s loss-driven sawtooth. In v3, loss explicitly helps constrain the model. Nor should “v3 uses ECN” be assumed: this draft leaves the precise ECN response unspecified, while requiring a connection that advertises ECN support to treat congestion-experienced marks as congestion. [3]

No version gets to escape the original measurement bargain. To learn that more capacity exists, a sender has to offer more traffic. If capacity does not exist, the experiment creates a queue or loss. To learn the unloaded delay, it has to make room for a low-delay observation. If it backs off too much, it wastes capacity; if other traffic refuses to back off, it may learn very little.

08 / What to carry away

The probe is part of the cost

Return to the file transfer. We want the bottleneck busy, but do not want every additional byte spending its life waiting for a turn. At the idealized knee, those goals coincide. Staying there is difficult because the knee moves, observations arrive late, and our own traffic changes the thing we are trying to observe.

That is the common tradeoff beneath these designs: how much temporary excess—or temporary underuse—will you accept to discover and maintain a useful operating point? Reno pays through growth and backoff around congestion feedback. Copa turns estimated delay into a changing target rate. CoDel decides when local waiting has lasted long enough to justify signaling. BBR separates rate from flight volume and deliberately alternates the conditions under which it measures the path. This is a comparison of their control logic, not a claim that their guarantees or outcomes are equivalent.

The deeper distinction is between choosing where you would like to operate and learning where you can operate while other people are learning too. Multiplying bandwidth by delay solves a small accounting problem. Most of congestion control is in the second problem.

One final case: did the model stick?

A path delivers 10 MB/s with 50 ms unloaded RTT. It now has 0.50 MB queued, and you pace at 10 MB/s. Predict the RTT, whether the queue drains, and how long a 7.5 MB/s drain would take.

Open the accounting

The queue adds 0.50 / 10 = 50 ms, so RTT is 100 ms. Pacing at 10 MB/s preserves it. Pacing at 7.5 MB/s drains at 2.5 MB/s, taking 0.50 / 2.5 = 0.20 seconds. In the fluid model, delivery remains 10 MB/s until the backlog is exhausted. If you got all three, you have the rate-versus-inventory distinction that makes BBR’s phases intelligible.

For the next read, open Linux’s BBRv1 implementation and follow bbr_main() into the model update, pacing-rate update, and window update. Ask what observation could make each estimate wrong. Then read the BBRv3 design overview to see which additional bounds and experiments the real network demanded.