1. Гайды
  2. Сервер с нуля

sysctl для ноды: BBR, буферы и очереди

Один файл, который превращает дефолтное ядро в ноду, способную переварить трафик. С объяснением, что каждая строка делает.

Актуально для Ubuntu 24.04 / Debian 12, ядро 6.xОбновлено 24.09.2026 4 мин чтения

Дефолтные настройки ядра рассчитаны на ноутбук, а не на ноду, через которую ходят сотни клиентов. Хорошая новость: чинится одним файлом и одной командой. Плохая: половина советов в интернете копируется с 2014 года и либо бесполезна, либо вредна.

Проверь, что BBR вообще есть

BBR живёт в ядре начиная с 4.9. В любом современном дистрибутиве он есть, но модуль может быть не загружен:

sysctl net.ipv4.tcp_available_congestion_control

Если в списке нет bbr:

modprobe tcp_bbr && echo tcp_bbr > /etc/modules-load.d/bbr.conf

Файл целиком

Кладём в отдельный файл, а не в /etc/sysctl.conf, чтобы обновления пакетов его не трогали:

/etc/sysctl.d/99-node.confini
# Очередь fq + BBR: меньше задержка, выше скорость на плохих каналах
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# Буферы сокетов до 64 МБ: нужно для гигабита на дальних маршрутах
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
# min / default / max: первые два числа — дефолты ядра, поднимаем только max
net.ipv4.tcp_rmem = 4096 131072 67108864
net.ipv4.tcp_wmem = 4096 16384 67108864

# Очереди соединений: чтобы всплеск клиентов не упирался в дефолтный somaxconn = 4096
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 250000

# Больше исходящих портов (дефолт 32768–60999) и переиспользование TIME-WAIT
# для исходящих соединений (дефолт tcp_tw_reuse = 2 — только для loopback)
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_tw_reuse = 1
# сколько осиротевшее соединение висит в FIN_WAIT_2 (дефолт 60 с)
net.ipv4.tcp_fin_timeout = 15

# Не сбрасывать окно после простоя (дефолт 1 — сбрасывать)
net.ipv4.tcp_slow_start_after_idle = 0
# Искать рабочий MTU сам, когда обнаружена ICMP-чёрная дыра (дефолт 0 — выключено)
net.ipv4.tcp_mtu_probing = 1
# TCP Fast Open для клиента и сервера (дефолт 1 — только клиент).
# Серверная часть работает, только если приложение включает TFO на своём сокете
net.ipv4.tcp_fastopen = 3

# Файловые дескрипторы: каждое соединение — это файл.
# file-max — на всю систему, nr_open — потолок на процесс (дефолт 1048576)
fs.file-max = 2097152
fs.nr_open = 2097152

Применяем без перезагрузки:

sysctl --system

И проверяем, что BBR взлетел:

sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc

Лимиты процессов

fs.file-max — это потолок на всю систему. У процесса свой лимит RLIMIT_NOFILE, и под systemd он по умолчанию 1024:524288: мягкий 1024, жёсткий 524288. Программа, которая сама не поднимает мягкий лимит, упрётся в too many open files примерно на тысяче соединений.

Xray и другие программы на Go (с Go 1.19) сами поднимают мягкий лимит до жёсткого, а юнит из официального установщика XTLS/Xray-install уже содержит LimitNOFILE=1000000. Свой лимит нужен, если сервис запускается своим юнитом или это не Go-программа. Для сервисов под systemd:

/etc/systemd/system/xray.service.d/limits.confini
[Service]
LimitNOFILE=1048576
systemctl daemon-reload && systemctl restart xray

Для Docker лимит задаётся в compose через ulimits: одним числом (nofile: 1048576) или парой soft/hard. LimitNOFILE не может быть больше fs.nr_open.

Conntrack

Если на ноде есть NAT, Docker или iptables с ct state, ядро помнит каждое соединение в таблице conntrack. Переполнится — новые соединения начнут молча отваливаться, а в dmesg появится nf_conntrack: table full, dropping packet.

/etc/sysctl.d/99-conntrack.confini
# дефолт равен nf_conntrack_buckets и зависит от объёма памяти
net.netfilter.nf_conntrack_max = 1048576
# дефолт 432000 (5 суток): мёртвые established-записи долго занимают таблицу
net.netfilter.nf_conntrack_tcp_timeout_established = 7200

Заметка

Параметры nf_conntrack появляются, только когда модуль загружен. Если sysctl --system ругается на них при загрузке, добавь nf_conntrack в /etc/modules-load.d/.

Чего не делать

Не делай так

net.ipv4.tcp_tw_recycle = 1. Этот параметр ломал клиентов за NAT и был удалён из ядра в 4.12. На современном ядре такого ключа просто нет, и sysctl ругается на него. Если он есть в твоём «оптимизационном скрипте», скрипт писали очень давно.

  • Не раздувай rmem_default/wmem_default и tcp_mem. rmem_default и wmem_default — стартовый буфер каждого сокета, и тысяча клиентов умножит его на тысячу. tcp_mem — общий лимит памяти TCP в страницах (не в байтах): ядро считает его при загрузке от объёма RAM, и если его задрать, TCP перестанет себя ограничивать, пока память не кончится.
  • Не отключай IPv6 через sysctl «на всякий случай». Часть софта ожидает ::1 и потом ломается загадочно. Если IPv6 не нужен, просто не слушай на нём.
  • Не копируй наборы целиком, не читая. Сравнить с тем, что используют в сообществе, можно в базе: sysctl, BBR, MTU.

Проверка до и после

Разницу видно на дальних маршрутах. Прогони iperf3 до сервера в другой стране до и после применения:

iperf3 -c <сервер> -t 20 -P 4

Если скорость не изменилась, узкое место не в ядре: смотри на канал хостера и CPU steal. Как это проверить — в чек-листе нового VPS.

Источники