Enable BBR on Debian 12 to make new TCP connections use BBR with the fq packet scheduler. The change takes about five minutes, applies without opening a port, and can be retained across reboots.
Prerequisites
- A Debian 12 (Bookworm) VPS with a kernel newer than 4.9; the standard Debian 12 kernel is in the 6.1 series.
- SSH access as
root, or a normal account that can run commands withsudo. - Permission to load kernel modules and write files below
/etc; an unprivileged container may not provide these capabilities. - No domain, web server, extra RAM, or extra disk is needed because BBR is a kernel-level TCP setting.
- If you need the server first, start with a Linux VPS from VPSLake and install Debian 12.
BBR changes TCP congestion control and packet pacing. It cannot raise your plan’s bandwidth limit, and the result depends on the route, the remote peer, and the workload.
Step 1: Confirm Debian 12 and inspect the current TCP settings
Check the release and running kernel before changing anything so a provider-supplied image or custom kernel does not surprise you.
cat /etc/debian_version
. /etc/os-release && printf '%s\n' "$PRETTY_NAME"
uname -r
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control
The release should identify Debian GNU/Linux 12 (Bookworm), and the kernel will normally start with 6.1. A new installation commonly uses cubic; the available-algorithm list may not include BBR until its module is loaded.
12.x
Debian GNU/Linux 12 (bookworm)
6.1.0-...-amd64
net.ipv4.tcp_available_congestion_control = reno cubic
net.ipv4.tcp_congestion_control = cubic
The point release, kernel revision, and algorithm order vary. Keep a newer provider kernel if it is working; the important requirement is that it exposes BBR.
Step 2: Load BBR for this boot
The kernel must register BBR before a sysctl can select it, so load the module and check the list immediately.
sudo modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control
The result should contain bbr:
net.ipv4.tcp_available_congestion_control = reno cubic bbr
If BBR was already listed, modprobe leaves it ready and the second command confirms that the running kernel can use it. The order of the names is not significant.
Step 3: Save the settings so they survive a reboot
Put the module request and sysctl values in separate local files; Debian’s boot services can then restore both pieces without changing unrelated configuration.
sudo tee /etc/modules-load.d/tcp-bbr.conf >/dev/null <<'EOF'
tcp_bbr
EOF
sudo tee /etc/sysctl.d/99-bbr-debian12.conf >/dev/null <<'EOF'
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF
sudo sysctl -p /etc/sysctl.d/99-bbr-debian12.conf
The first file asks the boot-time module loader to make BBR available. The second selects BBR for new TCP connections and sets fair queueing as the default scheduler; sysctl -p applies it now instead of waiting for a reboot.
Expected output:
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
Debian reads local kernel-parameter files from /etc/sysctl.d/; the Debian Reference documents this configuration model.
Step 4: Check the queue attached to the live network interface
net.core.default_qdisc controls the default for interfaces created later, but the interface already carrying your SSH session can still have another root queue.
ip -o route show default
MAIN_IFACE=$(ip -o route show default | awk '{print $5; exit}')
printf '%s\n' "$MAIN_IFACE"
tc qdisc show dev "$MAIN_IFACE"
The route output identifies the default device, often ens3 or eth0. If the final command does not show qdisc fq, replace the live root queue for the current boot:
sudo tc qdisc replace dev "$MAIN_IFACE" root fq
tc qdisc show dev "$MAIN_IFACE"
A successful check contains qdisc fq:
default via 203.0.113.1 dev ens3 proto dhcp src 203.0.113.20 metric 100
ens3
qdisc fq 0: root refcnt 2 limit 10000p buckets 1024
Your interface name and queue details will differ. This tc replacement is runtime state; the saved default_qdisc value handles future interface initialization and the next boot. Do not bring the network profile down over SSH unless you have the VPS control panel console available.
Verify BBR on Debian 12
Run the complete check below to confirm the active algorithm, persistent files, module availability, and qdisc on the interface used by the default route.
MAIN_IFACE=$(ip -o route show default | awk '{print $5; exit}')
sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc
sysctl net.ipv4.tcp_available_congestion_control
grep -Ev '^[[:space:]]*(#|$)' /etc/sysctl.d/99-bbr-debian12.conf
grep -Fx 'tcp_bbr' /etc/modules-load.d/tcp-bbr.conf
tc qdisc show dev "$MAIN_IFACE"
You want tcp_congestion_control = bbr, default_qdisc = fq, bbr in the available list, both assignments from 99-bbr-debian12.conf, the line tcp_bbr, and qdisc fq for the active device. These values affect connections created after the change, so reconnect SSH or restart a long-lived service before comparing traffic. The Linux kernel IP sysctl reference explains the TCP parameters used here.
Troubleshooting
tc: command not found
The minimal Debian image may not include iproute2, which provides both ip and tc. Install it, then repeat Step 4:
sudo apt-get update
sudo apt-get install -y iproute2
bbr is missing from the available algorithm list
The running kernel has not registered BBR. Try loading the module once; if modprobe reports that tcp_bbr is missing, install Debian’s standard kernel meta-package and reboot into it:
sudo modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control
sudo apt-get update
sudo apt-get install -y linux-image-amd64
sudo reboot
After reconnecting, run Step 2 again. If the machine still boots a provider-managed custom kernel, use the VPS control panel console and request a Debian 12 kernel with BBR support.
RTNETLINK answers: Operation not permitted
This usually means the VPS is an unprivileged container without the capability needed to replace a qdisc. The TCP sysctl may still work, but the live fq queue cannot be changed from inside that container; ask the provider for a full VPS or traffic-control permission.
The server returns to cubic after reboot
Another sysctl file may be loaded after your setting, or the module was not loaded early enough. Search the active configuration, then reload the module and the Debian 12 file:
grep -RnsE 'tcp_congestion_control|default_qdisc' /etc/sysctl.conf /etc/sysctl.d /usr/lib/sysctl.d 2>/dev/null
sudo systemctl restart systemd-modules-load
sudo sysctl -p /etc/sysctl.d/99-bbr-debian12.conf
sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_congestion_control
Keep the local file name beginning with 99- so it loads late in the normal sysctl ordering. If the provider kernel does not expose BBR, install and select Debian’s standard kernel as described above.
FAQ
Why is fq used with BBR?
BBR estimates capacity and paces packets, while fq provides per-flow fair queueing and lets the kernel pace those packets cleanly. Setting net.core.default_qdisc selects fq for newly initialized interfaces; Step 4 checks whether the current interface also needs a live replacement.
Why can BBR be available but not active?
Loading tcp_bbr only registers the algorithm. The net.ipv4.tcp_congestion_control = bbr assignment is what selects it for new TCP connections, so check that both the live sysctl and 99-bbr-debian12.conf contain BBR.
Can I enable BBR inside a Docker or LXC container?
Only when the container shares a host kernel that exposes BBR and grants the needed module/sysctl and traffic-control permissions. If modprobe or tc is denied, enable BBR on the host or use a VPS with full kernel networking access.
Will an existing SSH connection switch to BBR?
The setting is selected when TCP connections are created. Your current SSH session may keep its original congestion-control state; reconnect SSH or restart the service you are evaluating after verification succeeds.