Когда веб‑сервис «упирается» в сеть, симптомы часто выглядят одинаково: часть клиентов получает Connection refused, часть — таймауты, в логах появляются сообщения про переполненные очереди, а графики CPU и RAM при этом выглядят вполне прилично. Виноваты обычно не «плохой интернет» и не «слабый сервер», а несогласованные лимиты на очереди принятия соединений и количество файловых дескрипторов.
Разберём три узла, которые чаще всего требуют согласованной настройки: backlog в приложении (или веб‑сервере), системный потолок net.core.somaxconn, а также лимиты дескрипторов fs.file-max и ulimit для nofile. Отдельно затронем net.ipv4.tcp_max_syn_backlog, который влияет на поведение при всплесках новых TCP‑подключений.
Если хотите глубже про общую логику настройки сетевых параметров ядра, держите под рукой: практика sysctl для сетевого тюнинга Linux.
Модель очередей: где на самом деле возникает «очередь на вход»
В TCP сервер обычно делает три шага: socket() → bind() → listen(backlog). Параметр backlog задаёт размер очередей на стороне ядра для входящих соединений. Это важно: очереди живут в ядре, а не в процессе приложения.
На практике удобно мыслить двумя очередями:
- SYN backlog — очередь полуоткрытых соединений (SYN получен, рукопожатие ещё не завершено). На неё влияет
net.ipv4.tcp_max_syn_backlog. - Accept backlog — очередь уже установленных соединений, которые ждут, пока приложение вызовет
accept(). На неё влияютnet.core.somaxconnи параметрbacklogвlisten().
Если переполняется SYN backlog — клиенты чаще видят таймауты установки соединения (повторы SYN, рост времени connect). Если переполняется accept backlog — ядро может начать отклонять соединения, что часто выглядит как ECONNREFUSED или как резкий рост ошибок у балансировщика.
net.core.somaxconn и backlog: почему выставили 65535, а стало 4096
net.core.somaxconn — это системный предел (cap) для значения backlog в listen(). То есть приложение может попросить backlog хоть 100000, но ядро ограничит фактическое значение сверху net.core.somaxconn.
Практический вывод: «backlog tuning» — это всегда минимум из двух величин: listen(backlog) в приложении и net.core.somaxconn в ядре.
Проверяем текущие значения
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.ipv4.tcp_syncookies
Значение net.ipv4.tcp_syncookies обычно стоит держать включённым (1), но воспринимать как «страховку», а не как замену нормальным очередям и способности приложения принимать соединения.
Как увидеть, что очередь действительно переполняется
Смотрите статистику TCP и события переполнения listen‑очередей.
ss -s
netstat -s | sed -n '1,200p'
На современных системах удобно через nstat (из пакета iproute2):
nstat | egrep 'ListenOverflows|ListenDrops|TCPAbortOnMemory|TCPSynRetrans|TCPTimeouts'
Если растут ListenOverflows и ListenDrops — это почти прямое указание на проблемы с accept backlog (или на то, что приложение не успевает делать accept()).
Настройка somaxconn (временно и постоянно)
Временно (до перезагрузки):
sysctl -w net.core.somaxconn=4096
Постоянно — через файл в /etc/sysctl.d/:
printf '%s
' 'net.core.somaxconn = 4096' | tee /etc/sysctl.d/99-sockets-backlog.conf
sysctl --system
Частые рабочие значения: 1024, 4096, 8192. Очень большие числа «на всякий случай» обычно не ломают систему напрямую, но могут маскировать проблему в приложении (оно не успевает принимать соединения) и увеличивать потребление памяти на очереди в пике.
Если вы планируете тюнинг sysctl и лимитов systemd без ограничений окружения, удобнее делать это на отдельном сервере: на VDS вы полностью контролируете параметры ядра и service‑лимиты под вашу нагрузку.

tcp_max_syn_backlog: защита от всплесков новых соединений
net.ipv4.tcp_max_syn_backlog ограничивает размер очереди полуоткрытых соединений (SYN backlog). Она критична для сервисов, где много коротких коннектов: API, фронтенды без keepalive, сервисы за NAT/промежуточными устройствами, которые часто рвут соединения, а также при резких всплесках трафика.
Если у вас растут задержки именно на установке соединения (connect time), а не на обработке запроса, и параллельно видно увеличение повторов SYN, имеет смысл поднять tcp_max_syn_backlog.
sysctl -w net.ipv4.tcp_max_syn_backlog=8192
Постоянно:
printf '%s
' 'net.ipv4.tcp_max_syn_backlog = 8192' | tee -a /etc/sysctl.d/99-sockets-backlog.conf
sysctl --system
Важное уточнение про SYN cookies
SYN cookies помогают пережить переполнение SYN backlog, но это режим деградации. Если постоянно упираетесь в SYN backlog — сначала найдите причину всплесков и согласуйте очереди и параметры сервиса.
fs.file-max и ulimit nofile: почему «всё настроили», а сокеты не открываются
Даже если очереди backlog идеальны, приложение всё равно может «упереться» в файловые дескрипторы. В Linux сокет — это файловый дескриптор. Когда заканчиваются FD, начинаются ошибки уровня «Too many open files», нестабильность accept(), проблемы с логами/резолвингом/прокси‑коннектами — в зависимости от того, что именно не смогло открыть новый FD.
Лимиты здесь многослойные:
fs.file-max— системный лимит на число файловых дескрипторов во всей системе.ulimitдляnofile(RLIMIT_NOFILE) — лимит на количество открытых файлов для конкретного процесса/пользователя.- На systemd‑системах дополнительный «потолок» часто задаётся в unit через
LimitNOFILE.
Проверяем текущие значения и фактическое потребление
sysctl fs.file-max
cat /proc/sys/fs/file-nr
ulimit -n
Файл /proc/sys/fs/file-nr обычно содержит три числа: выделено, свободно (или unused) и максимум. Трактовка второго поля между версиями ядра менялась, но для практики важны первое и третье: приближается ли «выделено» к максимуму.
Быстро прикинуть, кто «съедает» FD (топ‑20 процессов):
ls -1 /proc | grep -E '^[0-9]+$' | while read p; do printf '%s ' "$p"; ls -1 /proc/$p/fd 2>/dev/null | wc -l; done | sort -k2 -n | tail -n 20
Поднимаем fs.file-max
Временно:
sysctl -w fs.file-max=2097152
Постоянно:
printf '%s
' 'fs.file-max = 2097152' | tee /etc/sysctl.d/99-fd-limits.conf
sysctl --system
Число выбирайте от профиля нагрузки. Для веб‑узла с большим количеством одновременных соединений (и ещё, например, с проксированием на апстримы) «миллион+» дескрипторов на систему — нормальная планка, если хватает памяти и вы понимаете, какие процессы реально могут их потреблять.
ulimit nofile: поднимаем лимит для сервиса (systemd)
Частая ловушка: вы увеличили fs.file-max, но процесс всё равно ограничен, например, 1024 или 4096. На systemd‑системах корректнее задавать лимит в unit‑файле.
Проверить лимит процесса можно так (подставьте PID):
cat /proc/PID/limits | sed -n '1,200p'
Настройка через systemd override (рекомендуется, чтобы не править пакетный unit):
systemctl edit nginx
Добавьте:
[Service]
LimitNOFILE=200000
Затем примените:
systemctl daemon-reload
systemctl restart nginx
Подход одинаковый для nginx, php-fpm, gunicorn, node, java и любых прокси: повышайте LimitNOFILE именно там, где работает процесс, который держит соединения.
Как связать всё вместе: практический чеклист «backlog tuning»
Смысл настройки — не «подкрутить все ручки вверх», а согласовать лимиты так, чтобы они не противоречили друг другу и соответствовали реальной модели нагрузки.
Шаг 1. Поймите, где именно узкое место: SYN или accept
- Растут
ListenOverflows/ListenDrops— смотритеnet.core.somaxconnи backlog приложения, плюс способность приложения быстро делатьaccept(). - Растут повторы SYN/таймауты коннекта — смотрите
net.ipv4.tcp_max_syn_backlog, состояние сети и профиль всплесков новых соединений.
Шаг 2. Проверьте backlog на стороне приложения/веб‑сервера
Например, у Nginx есть директива listen ... backlog=. У некоторых рантаймов и фреймворков backlog задаётся параметром или имеет консервативное значение по умолчанию. Если приложение просит backlog=128, то повышение net.core.somaxconn выше 128 само по себе не даст эффекта.
При этом помните: огромный backlog может увеличить «хвост» задержек при перегрузе — соединения будут дольше висеть в очереди, прежде чем их примут и обслужат. Иногда быстрый отказ лучше, чем «длинная очередь» и массовые таймауты.
Шаг 3. Убедитесь, что хватает FD
Типичная картина при нехватке FD: сервис перестаёт принимать новые соединения не потому, что очередь мала, а потому что ему нечем открыть новый сокет. Тогда можно бесконечно поднимать backlog, но проблема останется.
Мини‑проверка:
ulimit -n
sysctl fs.file-max
cat /proc/sys/fs/file-nr
Шаг 4. Нагрузочный тест и валидация метрик
После изменения параметров сделайте контролируемый тест (пусть даже короткий) и сравните метрики «до/после»:
- ошибки соединения у клиента/балансировщика;
ListenOverflows/ListenDrops;- время установления соединения (connect) и TTFB;
- количество открытых FD у процесса и по системе.
Если вы балансируете не HTTP, а TCP/UDP (например, базы, очереди, кастомные протоколы), полезно свериться с настройками стрим‑прокси: балансировка TCP/UDP в Nginx stream.
Частые ошибки и безопасные ориентиры
Ошибка 1: подняли somaxconn, но не изменили лимит на FD в сервисе
На systemd чаще всего «фактическая» проблема в LimitNOFILE, а не в fs.file-max. Начинайте с проверки /proc/PID/limits после рестарта сервиса.
Ошибка 2: лечат симптомы, а не причину перегруза
Переполнение accept backlog бывает из‑за того, что приложение не успевает обрабатывать запросы или слишком медленно вызывает accept(). Тогда повышение backlog лишь увеличит очередь ожидания, но не увеличит пропускную способность. Параллельно ищите «почему медленно»: блокировки, пул воркеров, база данных, диски, DNS, upstream‑задержки.
Ошибка 3: не учитывают, что соединение — это не один FD
В прокси‑сценариях (например, Nginx → upstream) один клиентский коннект часто означает минимум один FD на фронте плюс один на апстриме. Добавьте логи, файлы статики, резолвер, unix‑сокеты — и требование к ulimit для nofile растёт быстро.

Быстрый шаблон настроек для веб‑узла (как отправная точка)
Ниже — не «универсально лучший», а адекватный старт для сервера, который должен переживать пики новых подключений и держать много одновременных соединений. Дальше значения корректируются по метрикам.
cat > /etc/sysctl.d/99-sockets-and-fd.conf << 'EOF'
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
fs.file-max = 2097152
EOF
sysctl --system
Отдельно выставьте LimitNOFILE в unit ваших ключевых сервисов (веб‑сервер, приложение, прокси), затем проверьте, что процесс после рестарта действительно получил новый лимит через /proc/PID/limits.
Итог: что держать в голове
net.core.somaxconn и backlog решают разные части проблемы, но работают в связке: приложение просит очередь, ядро ограничивает. net.ipv4.tcp_max_syn_backlog помогает переживать всплески установления новых TCP‑соединений. А fs.file-max и ulimit для nofile определяют, сможет ли сервис вообще открывать новые сокеты и файлы под нагрузкой.
Самый практичный подход: зафиксировали симптомы, посмотрели nstat, проверили FD‑лимиты, согласовали значения, повторили тест. Так вы получите предсказуемое поведение при пиках, а не «магическую» настройку, которая работает только в спокойное время.


