{"id":985,"date":"2026-11-02T09:00:00","date_gmt":"2026-11-02T09:00:00","guid":{"rendered":"https:\/\/vpslake.com\/blog\/?p=985"},"modified":"2026-11-02T09:00:00","modified_gmt":"2026-11-02T09:00:00","slug":"enable-bbr-centos-stream-9","status":"publish","type":"post","link":"https:\/\/vpslake.com\/blog\/2026\/11\/02\/enable-bbr-centos-stream-9\/","title":{"rendered":"How to Enable BBR on CentOS Stream 9 (Step-by-Step)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">To enable BBR on CentOS Stream 9, load the kernel module, select BBR with <code>sysctl<\/code>, and pair it with the <code>fq<\/code> packet scheduler. The change takes about five minutes, works without opening a firewall port, and can be made persistent across reboots.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>A CentOS Stream 9 VPS with its standard 5.14-series kernel or another kernel that provides the <code>tcp_bbr<\/code> module.<\/li>\n\n\n<li>SSH access as <code>root<\/code>, or a regular account with working <code>sudo<\/code> privileges.<\/li>\n\n\n<li>Permission to load kernel modules, change kernel parameters, and modify files below <code>\/etc<\/code>; an unprivileged container may deny these operations.<\/li>\n\n\n<li>No domain, web server, extra RAM, or extra disk is required because BBR is a kernel-level TCP setting.<\/li>\n\n\n<li>If you need the server first, choose a <a href=\"https:\/\/vpslake.com\/buy-vps\">Linux VPS from VPSLake<\/a> and install CentOS Stream 9.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">BBR changes TCP congestion control and packet pacing. It cannot raise your VPS bandwidth limit, and the result depends on the route, the remote peer, and the traffic your application sends.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Check the CentOS release and current TCP settings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Confirm the release and running kernel before changing network parameters so you do not troubleshoot a provider image or custom kernel as if it were the standard installation.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat \/etc\/centos-release\nuname -r\nsysctl net.ipv4.tcp_available_congestion_control\nsysctl net.ipv4.tcp_congestion_control<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">CentOS Stream 9 should be identified on the first line. A standard image normally reports a 5.14 kernel; the active algorithm is often <code>cubic<\/code>, and <code>bbr<\/code> may not appear until its module is loaded.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CentOS Stream release 9 (Plow)\n5.14.0-...el9.x86_64\nnet.ipv4.tcp_available_congestion_control = reno cubic\nnet.ipv4.tcp_congestion_control = cubic<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The exact kernel revision and algorithm order vary as Stream receives updates. Keep a newer working provider kernel; it does not need to match the sample output.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Load BBR into the running kernel<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The kernel must register BBR before <code>sysctl<\/code> can select it, so load the module and check the available algorithms in the same SSH session.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo modprobe tcp_bbr\nsysctl net.ipv4.tcp_available_congestion_control<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>modprobe<\/code> is silent when it succeeds. The second command should now include <code>bbr<\/code>, although the order may differ:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>net.ipv4.tcp_available_congestion_control = reno cubic bbr<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Loading the module makes BBR available for this boot; it does not yet select BBR for new connections. If the command reports that the module cannot be found, stop here and use the first troubleshooting section rather than writing a setting that the kernel cannot apply.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Persist the module and congestion-control settings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Keep the boot-time module request separate from the sysctl file so each change is easy to inspect, undo, or diagnose after a kernel update.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo tee \/etc\/modules-load.d\/tcp-bbr.conf &gt;\/dev\/null &lt;&lt;'EOF'\ntcp_bbr\nEOF\nsudo tee \/etc\/sysctl.d\/99-tcp-bbr.conf &gt;\/dev\/null &lt;&lt;'EOF'\nnet.core.default_qdisc = fq\nnet.ipv4.tcp_congestion_control = bbr\nEOF\nsudo sysctl -p \/etc\/sysctl.d\/99-tcp-bbr.conf<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The first file asks <code>systemd-modules-load<\/code> to load <code>tcp_bbr<\/code> during future boots. The second selects BBR for new TCP connections and sets fair queueing as the default for interfaces initialized later; the final command applies both values immediately.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Expected output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>net.core.default_qdisc = fq\nnet.ipv4.tcp_congestion_control = bbr<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">CentOS Stream 9 follows the EL9 layout for persistent kernel parameters. See <a href=\"https:\/\/docs.redhat.com\/en\/documentation\/red_hat_enterprise_linux\/9\/html\/managing_monitoring_and_updating_the_kernel\/configuring-kernel-parameters-at-runtime\" target=\"_blank\" rel=\"noopener\">Red Hat\u2019s RHEL 9 kernel-parameter documentation<\/a> for the <code>\/etc\/sysctl.d\/<\/code> method.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Put <code>fq<\/code> on the active network interface<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Changing <code>net.core.default_qdisc<\/code> sets the default for interfaces created later, but an interface already carrying your SSH connection may still have the usual <code>fq_codel<\/code> queue.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MAIN_IFACE=$(ip -o route show default | awk '$1 == \"default\" {print $5; exit}')\nprintf '%s\\n' \"$MAIN_IFACE\"\ntc qdisc show dev \"$MAIN_IFACE\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The first command should print a device such as <code>ens3<\/code> or <code>eth0<\/code>. If the last command does not show <code>qdisc fq<\/code>, replace the live root queue for the current boot:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MAIN_IFACE=$(ip -o route show default | awk '$1 == \"default\" {print $5; exit}')\nsudo tc qdisc replace dev \"$MAIN_IFACE\" root fq\ntc qdisc show dev \"$MAIN_IFACE\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Look for <code>qdisc fq<\/code> in the result:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ens3\nqdisc fq 0: root refcnt 2 limit 10000p buckets 1024<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This runtime change can disappear when NetworkManager recreates the link. To retain it in the active NetworkManager profile, store that profile name and activate it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CONNECTION_NAME=$(nmcli -g GENERAL.CONNECTION device show \"$MAIN_IFACE\")\nprintf '%s\\n' \"$CONNECTION_NAME\"\nsudo nmcli connection modify \"$CONNECTION_NAME\" tc.qdiscs 'root fq'\nsudo nmcli connection up \"$CONNECTION_NAME\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The first command should print the active profile name. Bringing the connection up can briefly interrupt SSH, so use the VPS control panel\u2019s VNC console if you cannot risk losing the session. Red Hat\u2019s <a href=\"https:\/\/docs.redhat.com\/en\/documentation\/red_hat_enterprise_linux\/9\/html\/configuring_and_managing_networking\/linux-traffic-control_configuring-and-managing-networking\" target=\"_blank\" rel=\"noopener\">RHEL 9 traffic-control guide<\/a> documents the <code>tc.qdiscs<\/code> NetworkManager setting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verify BBR on CentOS Stream 9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Run one final check to confirm that BBR is active, the two configuration files contain the intended values, and the default-route interface is using <code>fq<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MAIN_IFACE=$(ip -o route show default | awk '$1 == \"default\" {print $5; exit}')\nsysctl net.ipv4.tcp_congestion_control net.core.default_qdisc\nsysctl net.ipv4.tcp_available_congestion_control\ngrep -Ev '^[[:space:]]*(#|$)' \/etc\/sysctl.d\/99-tcp-bbr.conf\ngrep -Fx 'tcp_bbr' \/etc\/modules-load.d\/tcp-bbr.conf\ntc qdisc show dev \"$MAIN_IFACE\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You want <code>tcp_congestion_control = bbr<\/code>, <code>default_qdisc = fq<\/code>, <code>bbr<\/code> in the available list, both assignments from <code>99-tcp-bbr.conf<\/code>, the line <code>tcp_bbr<\/code>, and <code>qdisc fq<\/code> for the active device. The algorithm is chosen when a TCP connection is created, so reconnect SSH or restart a long-lived service before comparing its traffic.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>net.ipv4.tcp_congestion_control = bbr\nnet.core.default_qdisc = fq\nnet.ipv4.tcp_available_congestion_control = reno cubic bbr\nnet.core.default_qdisc = fq\nnet.ipv4.tcp_congestion_control = bbr\ntcp_bbr\nqdisc fq 0: root refcnt 2 limit 10000p buckets 1024<\/code><\/pre>\n\n\n\n<aside class=\"vl-element vl-cta-banner\" aria-label=\"Tune CentOS Stream networking\">\n\t<div class=\"vl-cta-banner__content\">\n\t\t<p class=\"vl-cta-banner__eyebrow\">Linux VPS<\/p>\n\t\t<h3 class=\"vl-cta-banner__title\">Tune CentOS Stream networking<\/h3>\n\t\t<p class=\"vl-cta-banner__text\">Run CentOS Stream 9 workloads on a VPS ready for persistent TCP tuning.<\/p>\n\t<\/div>\n\t<a class=\"vl-cta-banner__button\" href=\"\/buy-vps\" aria-label=\"View Linux VPS plans\">View Linux VPS plans<\/a>\n<\/aside>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><code>modprobe: FATAL: Module tcp_bbr not found<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The running kernel does not expose BBR, or the matching module package is missing. Install the EL9 extra modules package, reboot into the newest installed kernel, and repeat Step 2:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y kernel-modules-extra\nsudo reboot<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After reconnecting, run <code>uname -r<\/code> and <code>sudo modprobe tcp_bbr<\/code> again. If DNF cannot provide a matching package or the VPS keeps booting a provider-managed custom kernel, ask the provider for a standard CentOS Stream 9 kernel with BBR support instead of copying a module from another kernel.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>sysctl: cannot set net.ipv4.tcp_congestion_control<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">BBR was selected before the module was registered. Load it, confirm that <code>bbr<\/code> appears, and apply the saved file again:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo modprobe tcp_bbr\nsysctl net.ipv4.tcp_available_congestion_control\nsudo sysctl -p \/etc\/sysctl.d\/99-tcp-bbr.conf<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the error returns after every reboot, check that <code>\/etc\/modules-load.d\/tcp-bbr.conf<\/code> contains exactly <code>tcp_bbr<\/code> and that <code>uname -r<\/code> still shows the kernel you repaired.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>RTNETLINK answers: Operation not permitted<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The VPS is likely an unprivileged container without the capability needed to change traffic control. BBR may still work as the active TCP algorithm, but the container cannot attach <code>fq<\/code>; the host administrator must apply that qdisc, or you need a full VPS with traffic-control permissions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">BBR changes back to <code>cubic<\/code> after a reboot<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Another sysctl file may be loaded later and override the local value, or the module was not loaded early enough. Find conflicting assignments, then reload the module and saved settings:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>grep -RnsE 'tcp_congestion_control|default_qdisc' \/etc\/sysctl.conf \/etc\/sysctl.d \/usr\/lib\/sysctl.d 2&gt;\/dev\/null\nsudo systemctl restart systemd-modules-load\nsudo sysctl -p \/etc\/sysctl.d\/99-tcp-bbr.conf\nsysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_congestion_control<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Remove or update the conflicting assignment rather than adding a second BBR file with another name.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Does enabling BBR increase my VPS bandwidth?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. BBR changes TCP pacing and capacity estimation, but it cannot raise your plan\u2019s network limit or make a slow remote server send faster. Compare the routes and applications that matter to you instead of assuming every transfer will improve.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do I need to change <code>firewalld<\/code> for BBR?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. BBR is a kernel setting and does not create a listener or open an inbound port. Leave your existing SSH and application rules in place.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Will BBR affect UDP traffic?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. The <code>net.ipv4.tcp_congestion_control<\/code> parameter applies to TCP only. UDP software must provide its own rate-control behavior, so this setting does not tune DNS, WireGuard, or other UDP services.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I return to CUBIC?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Change <code>net.ipv4.tcp_congestion_control = bbr<\/code> to <code>cubic<\/code> in <code>\/etc\/sysctl.d\/99-tcp-bbr.conf<\/code>, then run <code>sudo sysctl -p \/etc\/sysctl.d\/99-tcp-bbr.conf<\/code>. You can leave <code>tcp_bbr<\/code> in the module-load file because loading a module does not select it as the active algorithm.<\/p>\n\n\n\n<script type=\"application\/ld+json\">{\"@context\": \"https:\/\/schema.org\", \"@type\": \"FAQPage\", \"mainEntity\": [{\"@type\": \"Question\", \"name\": \"Does enabling BBR increase my VPS bandwidth?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"No. BBR changes TCP pacing and capacity estimation, but it cannot raise your plan\u2019s network limit or make a slow remote server send faster. Compare the routes and applications that matter to you instead of assuming every transfer will improve.\"}}, {\"@type\": \"Question\", \"name\": \"Do I need to change `firewalld` for BBR?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"No. BBR is a kernel setting and does not create a listener or open an inbound port. Leave your existing SSH and application rules in place.\"}}, {\"@type\": \"Question\", \"name\": \"Will BBR affect UDP traffic?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"No. The `net.ipv4.tcp_congestion_control` parameter applies to TCP only. UDP software must provide its own rate-control behavior, so this setting does not tune DNS, WireGuard, or other UDP services.\"}}, {\"@type\": \"Question\", \"name\": \"How do I return to CUBIC?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Change `net.ipv4.tcp_congestion_control = bbr` to `cubic` in `\/etc\/sysctl.d\/99-tcp-bbr.conf`, then run `sudo sysctl -p \/etc\/sysctl.d\/99-tcp-bbr.conf`. You can leave `tcp_bbr` in the module-load file because loading a module does not select it as the active algorithm.\"}}]}<\/script>\n\n","protected":false},"excerpt":{"rendered":"Enable BBR on CentOS Stream 9 with persistent fq settings, reboot-safe module loading, live checks, and fixes for common kernel errors on your VPS.","protected":false},"author":1,"featured_media":986,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_singular_sidebar":"","csco_page_header_type":"","csco_page_load_nextpost":"","footnotes":""},"categories":[5,9],"tags":[113,111,114,115],"class_list":["post-985","post","type-post","status-publish","format-standard","has-post-thumbnail","category-guides","category-vps-hosting","tag-bbr","tag-centos-stream-9","tag-linux-networking","tag-vps-tuning","cs-entry"],"_links":{"self":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/985","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/comments?post=985"}],"version-history":[{"count":1,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/985\/revisions"}],"predecessor-version":[{"id":3851,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/985\/revisions\/3851"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media\/986"}],"wp:attachment":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media?parent=985"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/categories?post=985"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/tags?post=985"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}