Add TCP BBR setup script with README and LICENSE

Bash script to automatically enable TCP BBR congestion control on Linux
servers. Supports Debian/Ubuntu/CentOS/RHEL/Alpine/Arch, includes
--check and --dry-run modes, sysctl backup, and verification.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 14:56:07 +03:00
co-authored by Claude Opus 4.5
commit a206c04ba9
3 changed files with 434 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 bbr-setup contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+85
View File
@@ -0,0 +1,85 @@
# bbr-setup
Bash script that enables [TCP BBR](https://github.com/google/bbr) congestion control on Linux servers with a single command.
## Quick start
```bash
curl -fsSL https://raw.githubusercontent.com/user/bbr-setup/main/setup.sh | sudo bash
```
Or clone and run locally:
```bash
git clone https://github.com/user/bbr-setup.git
cd bbr-setup
sudo bash setup.sh
```
## What it does
1. Verifies root permissions, distro, and kernel version (4.9+ required)
2. Checks that the `tcp_bbr` module is available
3. Backs up existing sysctl configuration
4. Writes BBR parameters to `/etc/sysctl.d/99-bbr.conf` (or `/etc/sysctl.conf` as fallback)
5. Applies and verifies the new settings
Parameters applied:
```
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
```
## Usage
```
bbr-setup 1.0.0 — enable TCP BBR congestion control
Usage: setup.sh [OPTIONS]
Options:
--check Show current BBR status and exit
--dry-run Show what would be done without making changes
-h, --help Show this help message
-v, --version Show version
```
### Examples
Apply BBR:
```bash
sudo bash setup.sh
```
Check current status without changing anything:
```bash
sudo bash setup.sh --check
```
Preview what would be changed:
```bash
sudo bash setup.sh --dry-run
```
## Supported distributions
- Debian / Ubuntu
- CentOS / RHEL / Fedora
- Alpine Linux
- Arch Linux
- Any Linux with kernel 4.9+ and the `tcp_bbr` module
## Exit codes
| Code | Meaning |
|------|---------|
| `0` | Success or BBR already enabled |
| `1` | Error (missing root, old kernel, module unavailable, verification failed) |
## License
[MIT](LICENSE)
Executable
+328
View File
@@ -0,0 +1,328 @@
#!/usr/bin/env bash
set -euo pipefail
# ---------------------------------------------------------------------------
# TCP BBR Setup Script
# Automatically enables BBR congestion control on Linux servers.
# ---------------------------------------------------------------------------
readonly VERSION="1.0.0"
readonly SYSCTL_DROP="/etc/sysctl.d/99-bbr.conf"
readonly SYSCTL_MAIN="/etc/sysctl.conf"
readonly MIN_KERNEL_MAJOR=4
readonly MIN_KERNEL_MINOR=9
# ── Modes ──────────────────────────────────────────────────────────────────
DRY_RUN=false
CHECK_ONLY=false
# ── Colors ─────────────────────────────────────────────────────────────────
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
else
RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET=''
fi
# ── Logging ────────────────────────────────────────────────────────────────
_ts() { date '+%H:%M:%S'; }
log_info() { printf "${GREEN}[%s] [INFO]${RESET} %s\n" "$(_ts)" "$*"; }
log_warn() { printf "${YELLOW}[%s] [WARN]${RESET} %s\n" "$(_ts)" "$*"; }
log_error() { printf "${RED}[%s] [ERROR]${RESET} %s\n" "$(_ts)" "$*" >&2; }
die() { log_error "$*"; exit 1; }
# ── Usage ──────────────────────────────────────────────────────────────────
usage() {
cat <<EOF
${BOLD}bbr-setup ${VERSION}${RESET} — enable TCP BBR congestion control
Usage: $(basename "$0") [OPTIONS]
Options:
--check Show current BBR status and exit
--dry-run Show what would be done without making changes
-h, --help Show this help message
-v, --version Show version
Examples:
sudo bash setup.sh # apply BBR
sudo bash setup.sh --check # status only
sudo bash setup.sh --dry-run # preview changes
EOF
exit 0
}
# ── Argument parsing ───────────────────────────────────────────────────────
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--check) CHECK_ONLY=true ;;
--dry-run) DRY_RUN=true ;;
-h|--help) usage ;;
-v|--version) echo "bbr-setup ${VERSION}"; exit 0 ;;
*) die "Unknown option: $1. Use --help for usage." ;;
esac
shift
done
}
# ── Detect distro ─────────────────────────────────────────────────────────
detect_distro() {
local distro="unknown"
if [[ -f /etc/os-release ]]; then
# shellcheck disable=SC1091
. /etc/os-release
distro="${ID}"
elif [[ -f /etc/redhat-release ]]; then
distro="rhel"
elif [[ -f /etc/alpine-release ]]; then
distro="alpine"
fi
echo "${distro}"
}
# ── Kernel version check ─────────────────────────────────────────────────
check_kernel() {
local kver
kver=$(uname -r)
local major minor
major=$(echo "$kver" | cut -d. -f1)
minor=$(echo "$kver" | cut -d. -f2)
log_info "Kernel version: ${kver}"
if (( major > MIN_KERNEL_MAJOR )) || { (( major == MIN_KERNEL_MAJOR )) && (( minor >= MIN_KERNEL_MINOR )); }; then
log_info "Kernel ${kver} meets minimum requirement (${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}+)"
else
die "Kernel ${kver} is too old. BBR requires ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}+"
fi
}
# ── Check BBR module availability ─────────────────────────────────────────
check_bbr_module() {
if lsmod | grep -q tcp_bbr; then
log_info "Module tcp_bbr is loaded"
return 0
fi
if modprobe -n tcp_bbr 2>/dev/null; then
log_info "Module tcp_bbr is available (not yet loaded)"
return 0
fi
# On newer kernels BBR can be built-in (not a module)
if [[ -f /proc/config.gz ]]; then
if zcat /proc/config.gz 2>/dev/null | grep -q 'CONFIG_TCP_CONG_BBR=y'; then
log_info "BBR is built into the kernel"
return 0
fi
fi
if sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -q bbr; then
log_info "BBR is listed as available congestion control"
return 0
fi
die "tcp_bbr module is not available on this system"
}
# ── Read current state ────────────────────────────────────────────────────
get_current_cc() { sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo "unknown"; }
get_current_qdisc() { sysctl -n net.core.default_qdisc 2>/dev/null || echo "unknown"; }
show_status() {
local cc qdisc
cc=$(get_current_cc)
qdisc=$(get_current_qdisc)
log_info "Current congestion control : ${BOLD}${cc}${RESET}"
log_info "Current default qdisc : ${BOLD}${qdisc}${RESET}"
if [[ "$cc" == "bbr" && ( "$qdisc" == "fq" || "$qdisc" == "fq_codel" ) ]]; then
return 0 # already configured
fi
return 1 # needs configuration
}
# ── Determine sysctl target file ─────────────────────────────────────────
pick_sysctl_file() {
if [[ -d /etc/sysctl.d ]]; then
echo "${SYSCTL_DROP}"
else
echo "${SYSCTL_MAIN}"
fi
}
# ── Backup ────────────────────────────────────────────────────────────────
backup_sysctl() {
local target="$1"
if [[ -f "$target" ]]; then
local bak="${target}.bak.$(date '+%Y%m%d_%H%M%S')"
cp -a "$target" "$bak"
log_info "Backup created: ${bak}"
echo "$bak"
fi
}
# ── Write sysctl parameters ──────────────────────────────────────────────
write_params() {
local target="$1"
local needs_cc=true
local needs_qdisc=true
if [[ -f "$target" ]]; then
grep -q '^net.core.default_qdisc\s*=\s*fq' "$target" && needs_qdisc=false
grep -q '^net.ipv4.tcp_congestion_control\s*=\s*bbr' "$target" && needs_cc=false
fi
# Also check the main sysctl.conf if we're writing to a drop-in
if [[ "$target" != "$SYSCTL_MAIN" && -f "$SYSCTL_MAIN" ]]; then
grep -q '^net.core.default_qdisc\s*=\s*fq' "$SYSCTL_MAIN" && needs_qdisc=false
grep -q '^net.ipv4.tcp_congestion_control\s*=\s*bbr' "$SYSCTL_MAIN" && needs_cc=false
fi
if ! $needs_cc && ! $needs_qdisc; then
log_info "Parameters already present in ${target}"
return 0
fi
{
echo ""
echo "# TCP BBR — added by bbr-setup ${VERSION} on $(date '+%Y-%m-%d %H:%M:%S')"
$needs_qdisc && echo "net.core.default_qdisc = fq"
$needs_cc && echo "net.ipv4.tcp_congestion_control = bbr"
} >> "$target"
log_info "Parameters written to ${target}"
}
# ── Apply & verify ────────────────────────────────────────────────────────
apply_sysctl() {
local target="$1"
# Load the bbr module if it isn't loaded yet
if ! lsmod | grep -q tcp_bbr; then
modprobe tcp_bbr 2>/dev/null || true
fi
sysctl -p "$target" >/dev/null 2>&1
# Also reload main if we used a drop-in
if [[ "$target" != "$SYSCTL_MAIN" && -f "$SYSCTL_MAIN" ]]; then
sysctl -p "$SYSCTL_MAIN" >/dev/null 2>&1 || true
fi
local new_cc new_qdisc
new_cc=$(get_current_cc)
new_qdisc=$(get_current_qdisc)
if [[ "$new_cc" == "bbr" ]]; then
log_info "Verification passed: congestion control = ${BOLD}${new_cc}${RESET}"
else
die "Verification failed: congestion control is '${new_cc}', expected 'bbr'"
fi
if [[ "$new_qdisc" == "fq" || "$new_qdisc" == "fq_codel" ]]; then
log_info "Verification passed: default qdisc = ${BOLD}${new_qdisc}${RESET}"
else
log_warn "Qdisc is '${new_qdisc}' (expected 'fq'). This may still work but fq is recommended."
fi
}
# ── Summary ───────────────────────────────────────────────────────────────
print_summary() {
local old_cc="$1" old_qdisc="$2" new_cc="$3" new_qdisc="$4" backup_path="${5:-}"
echo ""
printf "${BOLD}%-30s %-14s %-14s${RESET}\n" "" "Before" "After"
printf "%-30s %-14s %-14s\n" "Congestion control" "$old_cc" "$new_cc"
printf "%-30s %-14s %-14s\n" "Default qdisc" "$old_qdisc" "$new_qdisc"
echo ""
if [[ -n "$backup_path" ]]; then
log_info "Backup saved to: ${backup_path}"
fi
}
# ══════════════════════════════════════════════════════════════════════════
# Main
# ══════════════════════════════════════════════════════════════════════════
main() {
parse_args "$@"
echo ""
printf "${CYAN}${BOLD} TCP BBR Setup ${VERSION}${RESET}\n"
echo ""
# ── Root check ─────────────────────────────────────────────────────
if [[ $EUID -ne 0 ]]; then
die "This script must be run as root (use sudo)"
fi
# ── Distro detection ───────────────────────────────────────────────
local distro
distro=$(detect_distro)
log_info "Detected distro: ${BOLD}${distro}${RESET}"
# ── Kernel check ───────────────────────────────────────────────────
check_kernel
# ── BBR module check ───────────────────────────────────────────────
check_bbr_module
# ── Current state ──────────────────────────────────────────────────
local old_cc old_qdisc
old_cc=$(get_current_cc)
old_qdisc=$(get_current_qdisc)
if show_status; then
log_info "${GREEN}BBR is already enabled. Nothing to do.${RESET}"
print_summary "$old_cc" "$old_qdisc" "$old_cc" "$old_qdisc"
exit 0
fi
# ── Check-only mode ────────────────────────────────────────────────
if $CHECK_ONLY; then
log_warn "BBR is NOT currently enabled"
exit 1
fi
# ── Dry-run mode ───────────────────────────────────────────────────
local target
target=$(pick_sysctl_file)
if $DRY_RUN; then
log_warn "Dry-run mode — no changes will be made"
echo ""
log_info "Would write to: ${target}"
log_info "Parameters:"
echo " net.core.default_qdisc = fq"
echo " net.ipv4.tcp_congestion_control = bbr"
echo ""
exit 0
fi
# ── Apply changes ──────────────────────────────────────────────────
log_info "Configuring BBR..."
local backup_path=""
backup_path=$(backup_sysctl "$target")
write_params "$target"
apply_sysctl "$target"
local new_cc new_qdisc
new_cc=$(get_current_cc)
new_qdisc=$(get_current_qdisc)
print_summary "$old_cc" "$old_qdisc" "$new_cc" "$new_qdisc" "$backup_path"
log_info "${GREEN}${BOLD}Done.${RESET}"
}
main "$@"