За последние несколько лет я раз сорок открывал чужие sysctl.conf и каждый раз видел одно и то же: 80 строк, скопированных непонятно откуда, половина — устарела на пять ядер, четверть — конфликтует между собой, остальное — про IPv6, который у клиента отключён. Решил собрать свой минимальный набор, который реально влияет на боевые VPS под HTTP/прокси/туннели.

Что я НЕ ставлю

Сразу мимо:

  • net.ipv4.tcp_tw_recycle — выпилен из ядра 4.12, если он у вас есть в конфиге — конфиг древний
  • net.ipv4.tcp_syncookies=0 — отключать на боевом проде это диверсия
  • vm.swappiness=0 — про это уже сто раз писали, ставьте 10, не нолик
  • любые net.ipv4.tcp_*_timeout без понимания, что меняете

База, которую я ставлю всегда

# /etc/sysctl.d/99-tuning.conf
fs.file-max = 2097152
fs.nr_open = 1048576

net.core.somaxconn = 65535
net.core.netdev_max_backlog = 32768
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.default_qdisc = fq

net.ipv4.tcp_max_syn_backlog = 32768
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 6
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_notsent_lowat = 131072
net.ipv4.tcp_rmem = 4096 87380 33554432
net.ipv4.tcp_wmem = 4096 65536 33554432
net.ipv4.ip_local_port_range = 1024 65535

vm.swappiness = 10
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
vm.overcommit_memory = 1

Применяется через sysctl --system или sysctl -p /etc/sysctl.d/99-tuning.conf. Никаких ребутов.

Что делает каждый блок (коротко)

somaxconn и tcp_max_syn_backlog — это про accept-очередь. На дефолтных 4096 nginx начинает терять SYN при первом же спайке трафика. Проверяется так:

ss -lnt | head
nstat -az | grep -i listen

Если в ListenDrops и ListenOverflows цифры растут — значит, очередь переполняется.

tcp_slow_start_after_idle=0 — отключает повторный slow start после простоя соединения. Для keepalive-сессий между балансёром и бэкендами это даёт реальный выигрыш. По дефолту ядро при паузе >RTO сбрасывает cwnd, и следующая отправка идёт с минимума.

tcp_notsent_lowat=131072 — ограничивает данные, которые отдаются в socket-буфер до того, как ядро отправит их в сеть. Снижает буферный bloat для отправляющей стороны. Особенно важно для BBR.

ip_local_port_range = 1024 65535 — без этого исходящие соединения с балансёра упираются в дефолтные ~28k портов. Видел случаи, когда у клиента nginx-апстрим валился именно по эфемерным портам, а смотрели почему-то в conntrack.

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

fs.file-max и fs.nr_open без правильных limits.conf — бесполезны.

# /etc/security/limits.d/99-tuning.conf
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576

И для systemd-юнитов отдельно — limits.conf на них не действует:

# /etc/systemd/system/nginx.service.d/limits.conf
[Service]
LimitNOFILE=1048576
LimitNPROC=65535

Conntrack

Если на машине iptables/nftables с MASQUERADE или REDIRECT — conntrack-таблица переполняется первой. Симптом — в dmesg появляются nf_conntrack: table full, dropping packet. После этого NAT начинает терять соединения молча.

sysctl -w net.netfilter.nf_conntrack_max=2097152
sysctl -w net.netfilter.nf_conntrack_buckets=524288
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=7200

Размер хеш-таблицы (nf_conntrack_buckets) пишется в /sys/module/nf_conntrack/parameters/hashsize — через sysctl не везде применяется, проверяйте.

Что НЕ работает на VPS

Всё, что про IRQ-affinity, RPS/RFS, XPS — на виртуалке смысла почти нет. Виртуальная NIC обычно одна очередь, и /proc/interrupts показывает один CPU. На bare-metal — другой разговор, там настраивать обязательно.

Huge pages для веба тоже мимо. THP может даже ухудшить latency на nginx/redis из-за дефрагментации памяти. На редисе вообще рекомендуется отключать:

echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

Как проверить, что не сломал

После применения обязательно прогоните:

sysctl -a 2>/dev/null | grep -E 'somaxconn|conntrack_max|rmem_max'
ss -s
nstat -az | grep -iE 'drop|overflow|reject'
dmesg -T | tail -50

Если в dmesg посыпались сообщения про conntrack или сегменты TCP — откатывайте конкретную строку.

Итог

Тюнинг ядра — не про «больше цифр, лучше». Половина sysctl-параметров уже имеет адекватные дефолты в ядре 6.x. Реальный выигрыш дают пять-десять параметров, остальное — карго-культ. Применяйте по одному блоку, смотрите метрики сутки, потом следующий. Если налили всё сразу и сломалось — будете полдня выясывать, что именно.