To turn on TCP BBR congestion control, set two kernel parameters and they take effect immediately:
sudo sysctl -w net.core.default_qdisc=fq
sudo sysctl -w net.ipv4.tcp_congestion_control=bbrThat is the whole change. The first line sets the default queueing discipline to fq (fair queue), the second swaps the congestion-control algorithm from the kernel default (cubic) to bbr. BBR is a sending-side algorithm: only the machine doing the sending needs it, the client never has to know. No reboot, no library, no application change. To make it survive a reboot, drop those same two lines into a sysctl config file (covered below).
BBR (Bottleneck Bandwidth and Round-trip propagation time) is a congestion-control algorithm Google published in 2016 that models the path's bandwidth and RTT directly instead of treating packet loss as the congestion signal. On a long-haul link with even mild loss, that one difference can multiply throughput. It has been in the mainline Linux kernel since 4.9, so any reasonably current server already has it compiled in.
Check what you are running first
Before changing anything, see which algorithm is active and which ones the kernel can offer:
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_available_congestion_controlTypical output on a stock server:
net.ipv4.tcp_congestion_control = cubic
net.ipv4.tcp_available_congestion_control = reno cubic
If bbr does not appear in the available list, the module is not loaded yet. Load it and confirm:
sudo modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_controlnet.ipv4.tcp_available_congestion_control = reno cubic bbr
On kernels 4.9 and newer BBR is almost always built as a module (CONFIG_TCP_CONG_BBR=m) and modprobe pulls it in. Check your kernel version with uname -r; if it is older than 4.9, BBR is not there and no sysctl will conjure it.
Make it permanent
sysctl -w is in-memory only and resets on reboot. To persist the change, write it to a drop-in file under /etc/sysctl.d/:
echo 'net.core.default_qdisc = fq' | sudo tee /etc/sysctl.d/99-bbr.conf
echo 'net.ipv4.tcp_congestion_control = bbr' | sudo tee -a /etc/sysctl.d/99-bbr.conf
sudo sysctl --systemsysctl --system reloads every config under /etc/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.conf in order, so the new file applies without a reboot and also sticks across one. I prefer a dedicated 99-bbr.conf over editing /etc/sysctl.conf directly: it is one self-documenting file, easy to grep for, easy to remove if BBR turns out to be the wrong call for a given box.
Verify after the reload:
sysctl net.ipv4.tcp_congestion_control net.core.default_qdiscnet.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
Why the fq qdisc matters
The net.core.default_qdisc = fq line is not optional decoration. BBR paces its packets: instead of dumping a window of data into the NIC at once, it spreads transmissions out at a computed rate to match its estimate of the bottleneck bandwidth. The pacing is what keeps BBR from overflowing buffers.
In the kernel BBR was originally designed to lean on the fq (fair queue) qdisc to do that pacing in the network stack. Newer kernels added pacing inside TCP itself, so BBR can technically run without fq, but that internal fallback uses one high-resolution timer per TCP socket, which costs more on a box holding many connections, where fq paces in one place. Google's own deployment guidance and the kernel notes still pair bbr with fq, and fq adds per-flow fairness on top, which is what you want on a busy server. Set both. Do not set default_qdisc=fq on a machine that does not also use a pacing-aware algorithm and expect a benefit; the two go together.
You can check the qdisc actually attached to an interface with tc:
tc qdisc show dev eth0qdisc fq 8001: root refcnt 2 limit 10000p flow_limit 100p ...
What BBR actually buys you, and what it does not
BBR shines on a specific shape of problem: high bandwidth-delay product paths with non-congestive loss. That is the transcontinental or transoceanic link, the lossy mobile network, the path with a flaky middle hop. On those, loss-based algorithms like CUBIC read every dropped packet as "the network is full" and back off hard, collapsing throughput even though the loss came from line noise, not real congestion. BBR ignores loss as a signal and keeps sending at its measured rate, so it holds throughput where CUBIC would crater.
Where BBR does not help, or can hurt:
- Clean, short-RTT links (a server talking to clients in the same datacenter or region). CUBIC is already near-optimal there; BBR gives you nothing measurable.
- Fairness against CUBIC flows in a shared bottleneck. BBRv1, the version in mainline, can be aggressive toward concurrent CUBIC flows under some conditions, taking more than its fair share. Google addressed this in BBRv2 and the later BBRv3 work, but those live out of tree (see the
google/bbrrepo); mainline ships v1. - Deep-buffer paths where you want to fill the buffer. BBR deliberately keeps queues short. That is usually a feature (low latency), but it is a behavior change worth knowing.
Honest framing: BBR is a near-free win for an outbound-heavy public-facing server (web, API, media, file delivery) reaching a geographically spread audience. It is a no-op for an internal service on a clean LAN. Measure before and after with a real transfer over the real path, not a localhost loopback test that has no loss and no RTT to model.
BBR vs CUBIC at a glance
| Aspect | CUBIC (kernel default) | BBR |
|---|---|---|
| Congestion signal | Packet loss | Measured bandwidth + RTT |
| Best on | Clean, low-RTT paths | High-BDP, lossy long-haul paths |
| Reaction to non-congestive loss | Backs off hard, throughput drops | Largely ignores it, holds rate |
| Queue behavior | Tends to fill buffers (bufferbloat) | Keeps queues short, lower latency |
| Needs a pacing qdisc | No | Pairs with fq (recommended) |
| In mainline since | Long-standing default | Kernel 4.9 (2016) |
| Fairness vs CUBIC | n/a | v1 can be aggressive; v2/v3 fix it, out of tree |
Distro and kernel caveats
- Kernel version is the gate. BBR needs 4.9+. Debian 9+, Ubuntu 18.04+, RHEL/Rocky/Alma 8+, and any current cloud image all clear that bar. A genuinely old box might not.
- It is a module on most distros. If
bbris missing fromtcp_available_congestion_control, runsudo modprobe tcp_bbrfirst. To load it automatically at boot independent of the sysctl drop-in, add a file under/etc/modules-load.d/, though settingtcp_congestion_control = bbrvia sysctl normally triggers the load on its own. - Containers inherit the host. Congestion control and qdisc are host-kernel settings. Inside a container you cannot set a different algorithm than the host; change it on the host, and every container's TCP sends use it.
- No client-side change needed. Because BBR is sender-side, enabling it on your server speeds up data you send to clients (downloads, responses). It does nothing for data clients send to you unless they also run BBR.
See also
- Harden sshd by disabling password authentication: the other config-file change worth making on a fresh server, before you start tuning the network stack
- Watch live network throughput with nload: how I measure the before-and-after when I flip a server to BBR, so the throughput claim is something I see rather than assume
- External: google/bbr project, kernel IP sysctl docs, tc-fq(8) man page.
FAQ
Sources
Authoritative references this article was fact-checked against.





