How to Enable BBR on Ubuntu 26.04, 24.04 and 22.04

Long-distance uploads, backups, and hosted services can underuse fast links when CUBIC backs off. Ubuntu 26.04, 24.04, and 22.04 can switch locally sent TCP traffic to BBR with fq, then verify the live qdisc and return to each host's saved defaults when repeated tests show no benefit.

Last updatedAuthorJoshua JamesRead time7 minGuide typeUbuntu

When an Ubuntu host sends sustained traffic across a long network path, the default CUBIC congestion control algorithm can leave some available capacity unused. Enabling BBR on Ubuntu changes new, locally sent TCP connections to a model that estimates bottleneck bandwidth and round-trip time, then uses packet pacing to control the sending rate.

Ubuntu 26.04, 24.04, and 22.04 include the tcp_bbr and sch_fq modules on stock generic kernels. Save the host’s current queue discipline and congestion control setting before creating /etc/sysctl.d/99-bbr.conf; that baseline makes rollback specific to the machine instead of assuming cubic and fq_codel.

Decide Whether BBR Fits Your Workload

Google’s BBR project is designed for TCP senders. It is most relevant when this Ubuntu host pushes data over a path with a high bandwidth-delay product, such as remote backups, cross-region VPS traffic, file distribution, or a Jellyfin media server on Ubuntu or Plex Media Server on Ubuntu sending media to distant clients.

WorkloadBBR fitReason
Sustained outbound TCP over long or variable pathsWorth testingBBR can keep more data in flight without treating every loss event as congestion.
Low-latency LAN traffic, browsing, and short transfersUsually limited benefitThe connection may finish before congestion control becomes the bottleneck.
UDP traffic or a host that only receives downloadsNo direct benefitBBR controls locally sent TCP data; the remote sender controls an ordinary download.

BBR does not open ports, change listeners, or alter UFW firewall rules on Ubuntu. Bare-metal systems and full virtual machines normally control their own kernel modules and sysctls. Shared-kernel containers such as LXC or OpenVZ may require the host or VPS provider to enable BBR instead.

Enable BBR on Ubuntu

Confirm Kernel Support

Check whether the environment shares its kernel with a host:

systemd-detect-virt

A full virtual machine can report a hypervisor such as vmware, while bare metal reports none. A shared-kernel container requires host or provider control. On a bare-metal system or full VM, inspect the running Ubuntu kernel configuration:

grep -E '^CONFIG_(TCP_CONG_BBR|NET_SCH_FQ)=' "/boot/config-$(uname -r)"

Stock Ubuntu 26.04, 24.04, and 22.04 generic kernels return both options as modules:

CONFIG_TCP_CONG_BBR=m
CONFIG_NET_SCH_FQ=m

Confirm that both module files exist for the running kernel. Paths can end in .ko or .ko.zst, depending on the Ubuntu release and kernel package:

modinfo -F filename tcp_bbr
modinfo -F filename sch_fq

Load the congestion control and pacing modules, then check the available algorithms:

sudo modprobe tcp_bbr
sudo modprobe sch_fq
sysctl net.ipv4.tcp_available_congestion_control

The success criterion is that bbr appears in the list:

net.ipv4.tcp_available_congestion_control = reno cubic bbr

Back Up the Current Defaults and Create the BBR File

Display the current defaults and the qdisc already attached to the default-route interface. Keep this output for the final rollback comparison:

sysctl net.core.default_qdisc &&
sysctl net.ipv4.tcp_congestion_control &&
interface=$(ip route show default | awk 'NR == 1 {for (i = 1; i <= NF; i++) if ($i == "dev") {print $(i + 1); exit}}') &&
test -n "$interface" &&
tc qdisc show dev "$interface"

Continue only when tc shows an ordinary default-derived qdisc, such as fq_codel at the root or below mq. If it shows custom shaping such as htb, tbf, cake, or mqprio, the two-sysctl backup cannot recreate that topology. Use the configuration manager that owns the custom qdisc for rollback, and do not continue with this workflow.

Search every standard sysctl directory for existing assignments before adding another pair. The command can produce no output on a stock installation:

sudo grep -RnsE --include='*.conf' '^[[:space:]]*net\.(core\.default_qdisc|ipv4\.tcp_congestion_control)[[:space:]]*=' /etc/sysctl.conf /etc/sysctl.d /run/sysctl.d /usr/local/lib/sysctl.d /usr/lib/sysctl.d 2>/dev/null

If the command finds either key, review that file before continuing. Keep one intentional persistent assignment per key; otherwise a later file can silently override the BBR settings during boot.

Protect both paths with a guarded write. When both are unused, the command records the live values in a backup whose name does not end in .conf, so the sysctl loader ignores it, then creates the persistent BBR configuration:

write_bbr_files() {
    local config_file=/etc/sysctl.d/99-bbr.conf
    local backup_file=/etc/sysctl.d/99-bbr.conf.before-bbr
    local qdisc congestion_control

    if sudo test -e "$config_file" || sudo test -L "$config_file" ||
       sudo test -e "$backup_file" || sudo test -L "$backup_file"; then
        printf 'Stop: review the existing BBR files before continuing.\n' >&2
        return 1
    fi

    qdisc=$(sysctl -n net.core.default_qdisc) || return 1
    congestion_control=$(sysctl -n net.ipv4.tcp_congestion_control) || return 1
    case "$qdisc" in
        ''|*[!A-Za-z0-9_-]*) printf 'Invalid queue discipline value.\n' >&2; return 1 ;;
    esac
    case "$congestion_control" in
        ''|*[!A-Za-z0-9_-]*) printf 'Invalid congestion control value.\n' >&2; return 1 ;;
    esac

    if ! printf 'net.core.default_qdisc=%s\nnet.ipv4.tcp_congestion_control=%s\n' \
        "$qdisc" "$congestion_control" | sudo tee "$backup_file" >/dev/null; then
        sudo rm -f -- "$config_file" "$backup_file"
        return 1
    fi
    if ! printf '%s\n' \
        'net.core.default_qdisc=fq' \
        'net.ipv4.tcp_congestion_control=bbr' | sudo tee "$config_file" >/dev/null; then
        sudo rm -f -- "$config_file" "$backup_file"
        return 1
    fi
    return 0
}

write_bbr_files
write_status=$?
unset -f write_bbr_files
test "$write_status" -eq 0

Inspect both files before applying them. The backup should show the host’s original values, while the active file should contain fq and bbr:

sudo cat /etc/sysctl.d/99-bbr.conf.before-bbr
sudo cat /etc/sysctl.d/99-bbr.conf
net.core.default_qdisc=fq_codel
net.ipv4.tcp_congestion_control=cubic
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr

The first two lines are common Ubuntu defaults, not values to copy blindly. Your backup is authoritative if it differs.

Apply the Persistent BBR Settings

Apply only the dedicated BBR file. The duplicate-setting check prevents another known persistent assignment from hiding the result, while the reboot verifies actual boot-time precedence:

sudo sysctl -p /etc/sysctl.d/99-bbr.conf
sysctl net.core.default_qdisc
sysctl net.ipv4.tcp_congestion_control
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

New TCP connections select BBR immediately. However, net.core.default_qdisc=fq changes the default used when an interface qdisc is created; it does not replace an already running fq_codel qdisc. Reboot during a maintenance window to complete the persistent fq and BBR profile:

sudo reboot

Verify BBR After Reboot

Reconnect after the reboot and verify that the sysctl service succeeded, both values persisted, and the kernel still exposes BBR:

systemctl is-active systemd-sysctl.service
sysctl net.core.default_qdisc
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_available_congestion_control
active
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_available_congestion_control = reno cubic bbr

Inspect the qdisc attached to the interface used by the default route:

interface=$(ip route show default | awk 'NR == 1 {for (i = 1; i <= NF; i++) if ($i == "dev") {print $(i + 1); exit}}') &&
test -n "$interface" &&
printf 'Default-route interface: %s\n' "$interface" &&
tc qdisc show dev "$interface"

A single-queue interface should show fq as its root qdisc. A multiqueue interface can show mq at the root with fq on its child queues; both layouts prove that live egress traffic uses fq. Seeing only fq_codel means the interface has not adopted the new default.

Open a new SSH session or start a new outbound TCP transfer, then inspect established sockets:

ss -tin state established

Find the fresh connection and read its indented detail line. That line starts with bbr when the socket actually uses BBR. Connections opened before the change keep their original congestion control until they close.

Can You Enable BBR2 on Ubuntu?

Stock Ubuntu 26.04, 24.04, and 22.04 kernels expose bbr, not bbr2. Confirm the exact token on the running kernel before copying instructions written for a custom build:

sysctl net.ipv4.tcp_available_congestion_control
net.ipv4.tcp_available_congestion_control = reno cubic bbr

Do not set net.ipv4.tcp_congestion_control=bbr2 unless that command literally lists bbr2. Conversely, the token bbr alone does not identify a custom kernel’s BBR generation because modified kernels can keep the same registered algorithm name.

Compare BBR and CUBIC Performance

Use a workload where the tuned Ubuntu host sends TCP data: an outbound backup or upload, an iperf3 client run to a server you control, or a remote client downloading from a service on this host. A public download to this machine tests the remote server’s congestion control, not the local BBR setting.

Keep fq unchanged and switch only the congestion control algorithm. Start with CUBIC, close the tested connection, then open a fresh connection for each run:

sudo sysctl -w net.ipv4.tcp_congestion_control=cubic

Repeat the same route, endpoint, payload, and time window at least three times. Then select BBR and repeat with new connections:

sudo sysctl -w net.ipv4.tcp_congestion_control=bbr

Record throughput, transfer time, retransmissions, and application errors rather than relying on one peak result. An outbound curl upload to an endpoint you own can provide repeatable timing, while you can monitor bandwidth with Bmon on Ubuntu during a real workload. Keep BBR only when repeated measurements show a useful improvement without a workload regression.

Restore the Previous TCP Settings

Use the recorded backup instead of assuming the host began with cubic and fq_codel. Validate the backup, apply it to the live kernel, remove only /etc/sysctl.d/99-bbr.conf, and reboot:

backup_file=/etc/sysctl.d/99-bbr.conf.before-bbr
config_file=/etc/sysctl.d/99-bbr.conf

test "$(sudo awk 'END {print NR}' "$backup_file")" -eq 2 &&
sudo grep -Eq '^net\.core\.default_qdisc=[A-Za-z0-9_-]+$' "$backup_file" &&
sudo grep -Eq '^net\.ipv4\.tcp_congestion_control=[A-Za-z0-9_-]+$' "$backup_file" &&
sudo cat "$backup_file" &&
sudo sysctl -p "$backup_file" &&
sudo rm -f -- "$config_file" &&
sudo reboot

After reconnecting, compare the live values with the two lines in the backup and inspect the active qdisc:

sudo cat /etc/sysctl.d/99-bbr.conf.before-bbr &&
sysctl net.core.default_qdisc &&
sysctl net.ipv4.tcp_congestion_control &&
interface=$(ip route show default | awk 'NR == 1 {for (i = 1; i <= NF; i++) if ($i == "dev") {print $(i + 1); exit}}') &&
test -n "$interface" &&
tc qdisc show dev "$interface"

The live sysctl values must match the recorded values, and the active qdisc should match the original interface state. A standard Ubuntu baseline commonly returns to cubic with fq_codel. If the values differ, keep the backup and resolve the persistent sysctl assignment before cleanup.

When the comparison passes, remove the ignored backup file:

sudo rm -f -- /etc/sysctl.d/99-bbr.conf.before-bbr

No file under /etc/modules-load.d/ is required on stock Ubuntu 26.04, 24.04, or 22.04. Selecting the persistent sysctl values loads the modules automatically at boot.

Troubleshoot BBR on Ubuntu

BBR Does Not Appear in the Available List

Check the kernel configuration and module paths again:

grep -E '^CONFIG_(TCP_CONG_BBR|NET_SCH_FQ)=' "/boot/config-$(uname -r)"
modinfo -F filename tcp_bbr
modinfo -F filename sch_fq

On stock Ubuntu 26.04, 24.04, and 22.04 generic kernels, both configuration values should be m and both modinfo commands should print a path. If either file is missing, refresh package metadata and reinstall only the modules package that matches the running kernel:

sudo apt update
sudo apt install --reinstall "linux-modules-$(uname -r)"

Load both modules again and retest the available list:

sudo modprobe tcp_bbr
sudo modprobe sch_fq
sysctl net.ipv4.tcp_available_congestion_control

If the running kernel configuration omits either option, return to a supported Ubuntu generic kernel. Ubuntu 22.04 installations that need a maintained newer kernel can use the Ubuntu HWE kernel. A custom kernel can compile either feature directly as y, in which case no module file or modprobe step exists.

If systemd-detect-virt reports a shared-kernel container, do not continue inside the container; ask the host or provider to expose BBR. Reinstalling packages inside the container cannot add a module to the host kernel.

BBR Settings Revert After Reboot

Check the boot-time sysctl service and search for competing assignments with the grep command:

systemctl --no-pager --full status systemd-sysctl.service
sudo grep -RnsE --include='*.conf' '^[[:space:]]*net\.(core\.default_qdisc|ipv4\.tcp_congestion_control)[[:space:]]*=' /etc/sysctl.conf /etc/sysctl.d /run/sysctl.d /usr/local/lib/sysctl.d /usr/lib/sysctl.d 2>/dev/null

The service should be active with a successful result, and the intended BBR file should own both keys. Fix or remove only a duplicate file you control, then apply the BBR file and retest both values:

sudo sysctl -p /etc/sysctl.d/99-bbr.conf
sysctl net.core.default_qdisc
sysctl net.ipv4.tcp_congestion_control

Do not add a /etc/modules-load.d/bbr.conf workaround unless a separately diagnosed custom boot sequence requires it. On stock Ubuntu 26.04, 24.04, and 22.04, the sysctl configuration loads both modules automatically at boot.

The Default Is fq but the Interface Still Uses fq_codel

This mismatch is expected when the sysctl changed after the interface was created. Confirm both states:

sysctl net.core.default_qdisc &&
interface=$(ip route show default | awk 'NR == 1 {for (i = 1; i <= NF; i++) if ($i == "dev") {print $(i + 1); exit}}') &&
test -n "$interface" &&
tc qdisc show dev "$interface"

If the sysctl reports fq but tc shows only fq_codel, reboot during a maintenance window and repeat the sysctl and tc checks. Success is fq at the root or mq with fq child queues.

A Fresh Socket Does Not Show BBR

First confirm the default, then create a genuinely new connection and inspect established sockets:

sysctl net.ipv4.tcp_congestion_control
ss -tin state established

The sysctl must report bbr, and the detail line for the new socket must begin with bbr. Close and reopen the connection if it began before the change. If only one service is affected, inspect service logs with journalctl for the relevant systemd unit rather than treating application timeouts as kernel BBR messages.

Performance or Stability Regresses

Repeat the controlled CUBIC and BBR comparison with fresh connections. Check the affected service logs, disk or application limits, remote endpoint capacity, and the real interface qdisc before attributing the result to congestion control. If CUBIC repeatedly performs better for the actual workload, restore the saved host baseline and keep the original settings.

Conclusion

Ubuntu now selects bbr for new TCP connections and instantiates fq for paced egress traffic after reboot. Verify both the sysctl defaults and the real interface qdisc, then judge the change with repeated sender-side measurements. The saved baseline provides a host-specific rollback path when BBR does not improve the workload.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show our tutorials more often in Top Stories and mark them as preferred in AI Mode and AI Overviews when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee

2 thoughts on “How to Enable BBR on Ubuntu 26.04, 24.04 and 22.04”

Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="https://example.com">link</a> link
<blockquote>quote</blockquote> quote block

Add to the discussion

Questions, fixes, command output, and version notes help keep this guide current.

Verify before posting: