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:
@@ -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.
|
||||||
@@ -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)
|
||||||
@@ -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 "$@"
|
||||||
Reference in New Issue
Block a user