Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

Linux TCP backlog на VDS: somaxconn, tcp_max_syn_backlog, Nginx и PHP-FPM

Когда сайт получает всплеск соединений, узкое место может быть не в CPU, а в очередях TCP. Поясняем, как увидеть переполнение backlog, безопасно поднять лимиты ядра и согласовать Nginx с PHP-FPM.
Linux TCP backlog на VDS: somaxconn, tcp_max_syn_backlog, Nginx и PHP-FPM

TCP backlog часто вспоминают в момент, когда сайт «как будто жив»: CPU не упёрся в 100%, памяти хватает, база отвечает, но часть клиентов получает таймауты или редкие 502/504. На VDS это особенно заметно при коротких пиках: рекламная рассылка, индексация, массовое обновление приложения, всплеск ботов, прогрев кеша после деплоя. Очередь соединений заполняется быстрее, чем приложение успевает их принять, и проблема выглядит не как классическая нехватка ресурсов, а как потеря входящих TCP-соединений.

Разберём, что на практике означают tcp backlog, somaxconn, tcp_max_syn_backlog, accept queue и syn queue, как диагностировать их через ss, netstat и nstat, а также как согласовать настройки ядра Linux, Nginx и PHP-FPM. Это не набор «волшебных sysctl»: сначала измеряем, затем меняем лимиты, потом проверяем, что узкое место действительно ушло.

Две очереди TCP: syn queue и accept queue

Когда клиент открывает TCP-соединение к серверу, происходит трёхстороннее рукопожатие: SYN, SYN-ACK, ACK. На стороне Linux в этом процессе участвуют две разные очереди, которые часто смешивают в одну фразу «backlog забился».

syn queue — очередь полуоткрытых соединений. В ней находятся клиенты, которые отправили SYN, получили или должны получить SYN-ACK, но ещё не завершили рукопожатие финальным ACK. Размер этой очереди связан с параметром net.ipv4.tcp_max_syn_backlog. Если она переполняется, новые попытки подключения могут отбрасываться или обрабатываться через SYN cookies, если они включены.

accept queue — очередь уже установленных соединений, которые ядро готово отдать приложению через системный вызов accept(). Здесь клиентское TCP-соединение уже считается established, но Nginx, PHP-FPM или другой процесс ещё не забрал его из очереди. Верхняя граница этой очереди задаётся приложением в listen(backlog), но Linux дополнительно ограничивает её глобальным net.core.somaxconn.

Коротко: tcp_max_syn_backlog влияет на очередь до завершения TCP handshake, а somaxconn ограничивает очередь уже готовых к принятию соединений.

Для веб-стека Nginx/PHP-FPM важны обе очереди, но проявления разные. Если забивается syn queue, клиенты могут долго ждать установления TCP-соединения. Если забивается accept queue, соединение формально установлено, но приложение не успевает его принять, и дальше начинаются задержки, сбросы или всплески ошибок на уровне reverse proxy.

Где в цепочке появляются backlog: ядро, Nginx, PHP-FPM

В типичной LEMP-схеме есть минимум два места, где создаются слушающие сокеты. Первое — Nginx слушает публичные порты 80 и 443. Второе — PHP-FPM слушает Unix-сокет или TCP-порт, к которому Nginx отправляет FastCGI-запросы. У каждого слушающего сокета есть свой backlog.

Для Nginx параметр задаётся в директиве listen через backlog. Например: listen 443 ssl backlog=4096;. Но если net.core.somaxconn равен 128, приложение может попросить 4096, а ядро всё равно ограничит очередь меньшим значением. Поэтому настройка только Nginx без проверки sysctl часто не даёт ожидаемого результата.

Для PHP-FPM аналогичная настройка называется listen.backlog и задаётся в конфигурации пула. Если Nginx быстро принимает клиентские соединения, но PHP-FPM не успевает принимать FastCGI-соединения, очередь может забиться уже на внутреннем сокете PHP-FPM. Снаружи это будет выглядеть как 502, 504, рост upstream_response_time, сообщения вида connect() to unix:/run/php/php-fpm.sock failed или задержки при обращении к динамическим страницам.

Важно понимать: backlog не ускоряет приложение. Он даёт буфер на короткий пик соединений. Если PHP-воркеров мало, база медленная или каждый запрос держит процесс несколько секунд, увеличение backlog только отложит момент отказа. Настройка очередей должна идти вместе с анализом воркеров, keepalive, лимитов файловых дескрипторов и времени ответа приложения.

Схема syn queue и accept queue при подключении к веб-серверу

Быстрая диагностика через ss

Для Linux основной инструмент — ss из пакета iproute2. Он показывает слушающие сокеты и текущую заполненность очередей. На большинстве современных дистрибутивов ss уже установлен.

ss -ltn
ss -ltnp
ss -lxnp

Для TCP-сокетов в состоянии LISTEN у ss важны два столбца: Recv-Q и Send-Q. В этом контексте Recv-Q — сколько соединений сейчас ждёт принятия приложением в accept queue, а Send-Q — максимальный backlog для этого слушающего сокета.

State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0      4096   0.0.0.0:443       0.0.0.0:*     users:((nginx,pid=911,fd=7))
LISTEN 37     128    127.0.0.1:9000    0.0.0.0:*     users:((php-fpm,pid=1204,fd=9))

В примере Nginx имеет backlog 4096 и пустую accept queue, а PHP-FPM слушает TCP-порт 9000 с лимитом 128, при этом уже 37 соединений ждут принятия. Если во время пика Recv-Q у PHP-FPM часто приближается к Send-Q, проблема не в публичном TCP backlog Nginx, а во внутреннем upstream-слое.

Для Unix-сокетов PHP-FPM используйте ss -lxnp. Вы увидите путь к сокету и те же поля очереди, если ядро может их отобразить для конкретного типа сокета.

ss -lxnp | grep php

Чтобы оценить syn queue, посмотрите количество соединений в состоянии SYN-RECV для нужного порта. Единичные значения нормальны, а постоянные сотни или тысячи во время жалоб пользователей — повод проверять tcp_max_syn_backlog, SYN cookies, фильтрацию и характер входящего трафика.

ss -ant state syn-recv
ss -ant state syn-recv sport = :443
ss -ant state established sport = :443 | wc -l

Команда с фильтром по sport показывает соединения, где локальный серверный порт — 443. Для HTTP на 80 замените порт соответственно. На сильно нагруженном сервере запускайте такие команды аккуратно: сам сбор большого списка соединений тоже потребляет CPU.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

Что смотреть в netstat и счётчиках ядра

netstat считается устаревшим, но всё ещё полезен для счётчиков TCP-стека, особенно если вы привыкли к netstat -s. Если утилиты нет, установите пакет net-tools.

Debian/Ubuntu

apt update
apt install net-tools

RHEL-based: AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux

dnf install net-tools

Fedora

dnf install net-tools

После установки можно посмотреть статистику TCP:

netstat -s | grep -i listen
netstat -s | grep -i syn

Более прямой источник — /proc/net/netstat. В нём есть расширенные счётчики TCP, включая ListenOverflows и ListenDrops. Они помогают понять, были ли переполнения accept queue с момента старта системы.

grep -E 'ListenOverflows|ListenDrops|Syncookies' /proc/net/netstat
nstat -az | grep -E 'ListenOverflows|ListenDrops|Syncookies'

Смысл ключевых счётчиков:

  • ListenOverflows — ядро фиксировало переполнение listen/accept queue.
  • ListenDrops — соединения были отброшены на этапе обработки listen-сокета.
  • SyncookiesSent — Linux отправлял SYN cookies, что может быть нормальной защитной реакцией при переполнении SYN queue или SYN flood.

Счётчики накопительные. Разовый ненулевой показатель после месяцев аптайма не доказывает текущую проблему. Удобнее снять значения, подождать период нагрузки и снять снова.

nstat -az | grep -E 'ListenOverflows|ListenDrops|SyncookiesSent'
sleep 60
nstat -az | grep -E 'ListenOverflows|ListenDrops|SyncookiesSent'

Если значения растут во время жалоб пользователей, это уже предметный сигнал. Дальше нужно сопоставить его с ss -ltnp, логами Nginx, статусом PHP-FPM и метриками CPU, памяти и диска. Для сетевой картины также полезно настроить постоянный сбор метрик: об этом подробнее в материале про мониторинг и ограничение трафика на VDS.

Текущие значения sysctl и безопасный базовый профиль

Посмотреть текущие значения можно так:

sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.ipv4.tcp_syncookies
sysctl net.ipv4.tcp_abort_on_overflow

На современных ядрах net.core.somaxconn часто уже равен 4096, но на старых системах можно встретить 128. net.ipv4.tcp_max_syn_backlog зависит от версии ядра и доступной памяти. Для небольшого VDS с веб-нагрузкой разумно начинать не с экстремальных значений, а с умеренного профиля, который даёт запас на пики и не маскирует полностью проблемы приложения.

Создайте отдельный файл, чтобы не смешивать свои настройки с системными:

nano /etc/sysctl.d/90-tcp-backlog.conf
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_abort_on_overflow = 0

Применить настройки:

sysctl --system
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.ipv4.tcp_syncookies net.ipv4.tcp_abort_on_overflow

Почему не стоит сразу ставить 65535 «на всякий случай»? Большой backlog потребляет память под структуры ядра и может увеличивать время, в течение которого клиенты висят в очереди, пока приложение фактически не справляется. Для публичного сайта это иногда хуже: пользователь ждёт дольше, а мониторинг видит меньше явных отказов. Начинайте с 4096/8192, наблюдайте, затем повышайте при подтверждённой необходимости.

net.ipv4.tcp_abort_on_overflow почти всегда оставляют 0. При 1 Linux может отправлять RST, если accept queue переполнена. Это делает отказ быстрым, но для веб-сервисов обычно приводит к более заметным ошибкам у клиентов. Включать этот параметр стоит только осознанно и после тестов.

Настройка somaxconn и tcp_max_syn_backlog в Linux

Настройка Nginx backlog

Сначала проверьте фактическую конфигурацию Nginx. Нас интересуют директивы listen в серверных блоках. Если один и тот же адрес:порт описан в нескольких server, параметры слушающего сокета должны быть согласованы, иначе можно получить предупреждения или неочевидное поведение.

nginx -T | grep -E 'listen .*80|listen .*443'

Пример настройки для HTTPS:

server {
    listen 443 ssl http2 backlog=4096;
    listen [::]:443 ssl http2 backlog=4096;
    server_name example.com;

    root /var/www/example.com/public;
    index index.php index.html;
}

Для HTTP:

server {
    listen 80 backlog=4096;
    listen [::]:80 backlog=4096;
    server_name example.com;

    return 301 https://example.com$request_uri;
}

После изменения проверьте синтаксис и перезагрузите Nginx без разрыва активных соединений:

nginx -t
systemctl reload nginx
ss -ltnp | grep -E ':80|:443'

Если в ss у Nginx в Send-Q по-прежнему видно 128 или 511 вместо ожидаемых 4096, проверьте net.core.somaxconn, совпадение всех директив listen для адреса и то, что перезагрузка действительно прошла успешно.

Отдельно про reuseport. Директива listen 443 ssl http2 reuseport backlog=4096; создаёт отдельный слушающий сокет на каждый worker process и может уменьшить конкуренцию между воркерами на высоком RPS. Но это не универсальная таблетка от переполнения backlog. Включайте reuseport после тестов, потому что меняется распределение соединений между воркерами, а вместе с ним — профиль нагрузки и диагностика.

Настройка PHP-FPM listen backlog

У PHP-FPM backlog задаётся на уровне пула. Путь к конфигурации зависит от дистрибутива и версии PHP.

Debian/Ubuntu

ls /etc/php/*/fpm/pool.d/
grep -R 'listen.backlog' /etc/php/*/fpm/pool.d/

RHEL-based и Fedora

ls /etc/php-fpm.d/
grep -R 'listen.backlog' /etc/php-fpm.d/

Пример пула PHP-FPM с Unix-сокетом:

[www]
user = www-data
group = www-data
listen = /run/php/php-fpm-www.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
listen.backlog = 4096
pm = dynamic
pm.max_children = 32
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 8

Для RHEL-based систем пользователь и группа часто будут nginx или apache, а путь к сокету — /run/php-fpm/www.sock. Не копируйте пример вслепую: проверьте, под каким пользователем работает Nginx и какой сокет указан в fastcgi_pass.

ps -o user,group,comm -C nginx
nginx -T | grep fastcgi_pass

После изменения PHP-FPM перезагрузите сервис. Имя зависит от системы и версии PHP.

Debian/Ubuntu

systemctl reload php8.3-fpm
systemctl status php8.3-fpm --no-pager

RHEL-based и Fedora

systemctl reload php-fpm
systemctl status php-fpm --no-pager

Проверьте очередь PHP-FPM:

ss -lxnp | grep php
ss -ltnp | grep php-fpm

Если listen.backlog увеличен, но очередь всё равно регулярно заполняется, смотрите не только backlog. Возможно, pm.max_children слишком мал, PHP-запросы долго ждут базу, включена медленная внешняя интеграция, забит диск логами или OPcache не прогрет после деплоя. Backlog помогает пережить всплеск входящих соединений, но не заменяет capacity planning.

Как отличить backlog-проблему от нехватки PHP-воркеров

Переполнение accept queue и нехватка PHP-воркеров часто идут рядом, но лечатся по-разному. Если у PHP-FPM закончились свободные процессы, Nginx может продолжать принимать клиентские соединения, но запросы к FastCGI будут ждать. Если при этом listen.backlog маленький, внутренний сокет PHP-FPM начнёт отбрасывать подключения быстрее.

Включите статус PHP-FPM для технического location, закрытого от внешнего доступа, и смотрите показатели listen queue, max listen queue, active processes, max active processes. В самой конфигурации пула это выглядит так:

[www]
pm.status_path = /fpm-status
ping.path = /fpm-ping

Пример внутреннего location в Nginx:

location = /fpm-status {
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php-fpm-www.sock;
}

Проверка с сервера:

curl -s http://127.0.0.1/fpm-status
curl -s 'http://127.0.0.1/fpm-status?full'

Если listen queue растёт, а active processes постоянно равно pm.max_children, нужно увеличивать пул или ускорять приложение. Если listen queue растёт, но активных процессов мало, ищите проблему с сокетом, правами, зависшими воркерами, лимитами файловых дескрипторов или неправильным маршрутом fastcgi_pass.

Проверка лимитов файловых дескрипторов

Большой backlog бесполезен, если процессы упираются в лимит открытых файлов. TCP-сокет в Linux — это файловый дескриптор. Для Nginx и PHP-FPM проверьте системные лимиты и фактические значения у процесса.

systemctl show nginx -p LimitNOFILE
systemctl show php8.3-fpm -p LimitNOFILE
systemctl show php-fpm -p LimitNOFILE
cat /proc/$(pidof nginx | awk '{print $1}')/limits

Для Nginx есть директива worker_rlimit_nofile, а число одновременных соединений ограничивается worker_processes и worker_connections. Пример фрагмента:

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 8192;
    multi_accept on;
}

multi_accept on заставляет worker принимать сразу несколько новых соединений за один проход event loop. На высоких пиках это может быстрее разгребать accept queue, но иногда ухудшает справедливость распределения между воркерами. Как и reuseport, проверяйте под своей нагрузкой.

Для systemd-лимита создайте drop-in. Пример для Nginx:

systemctl edit nginx
[Service]
LimitNOFILE=65535

Затем примените изменения:

systemctl daemon-reload
systemctl restart nginx
systemctl show nginx -p LimitNOFILE

Для PHP-FPM действуйте аналогично, но учитывайте имя сервиса: php8.3-fpm на Debian/Ubuntu или php-fpm на RHEL-based системах. Если хотите глубже разобраться с unit-файлами и ограничениями ресурсов, посмотрите инструкцию про лимиты CPU, памяти и файловых дескрипторов в systemd.

Практический сценарий tuning Linux VDS

Разберём безопасную последовательность действий для VDS с Nginx и PHP-FPM, где при пиках появляются таймауты и редкие 502.

  1. Снимите базовые значения: sysctl, ss -ltnp, ss -lxnp, nstat, логи Nginx и PHP-FPM.
  2. Во время нагрузки проверьте, у какого сокета Recv-Q приближается к Send-Q: Nginx на 80/443 или PHP-FPM на Unix/TCP-сокете.
  3. Если растут ListenOverflows и ListenDrops, увеличьте somaxconn и backlog конкретного сервиса.
  4. Если много SYN-RECV и растут SYN cookies, проверьте tcp_max_syn_backlog, firewall, характер трафика и сетевые метрики.
  5. Если PHP-FPM упирается в pm.max_children, считайте память на процесс и корректируйте пул, а не только listen.backlog.
  6. После изменений повторите замеры и сравните не только ошибки, но и задержки: p95/p99, request_time, upstream_response_time.

Минимальный набор команд для снимка состояния:

date
uptime
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.ipv4.tcp_syncookies
ss -ltnp
ss -lxnp | grep -E 'php|fpm|nginx'
ss -ant state syn-recv | wc -l
nstat -az | grep -E 'ListenOverflows|ListenDrops|SyncookiesSent'
journalctl -u nginx --since '15 minutes ago' --no-pager
journalctl -u php8.3-fpm --since '15 minutes ago' --no-pager
journalctl -u php-fpm --since '15 minutes ago' --no-pager

Команды для журналов PHP-FPM приведены обеими строками, потому что имя сервиса различается. Ошибка в одной из них нормальна, если такого unit на вашей системе нет.

Типовые значения для небольших и средних VDS

Нет универсальной таблицы, которая подходит всем сайтам. Но для ориентира можно использовать такие стартовые значения:

  • небольшой сайт или админка: somaxconn=1024, tcp_max_syn_backlog=2048, Nginx/PHP-FPM backlog 1024;
  • обычный production-сайт на 2–4 vCPU: somaxconn=4096, tcp_max_syn_backlog=8192, Nginx/PHP-FPM backlog 4096;
  • нагруженный API с короткими соединениями: somaxconn=8192 или выше, tcp_max_syn_backlog=16384 или выше, но только после нагрузочного теста и проверки памяти.

Если у вас включён HTTP/2 или HTTP/3 на внешнем контуре, количество TCP-соединений может быть меньше, чем при HTTP/1.1 без keepalive, потому что один клиент переиспользует соединение активнее. Но PHP-FPM всё равно получает отдельную внутреннюю нагрузку от динамических запросов, поэтому php-fpm listen backlog остаётся важным.

Для сайтов за CDN или reverse proxy картина тоже меняется: к VDS подключается меньше уникальных IP, но каждый edge-узел может создавать много параллельных соединений. В логах это выглядит спокойно, а в ss видны плотные пачки established-соединений от ограниченного набора адресов.

Частые ошибки при настройке backlog

Первая ошибка — менять только somaxconn. Это глобальный потолок ядра, но приложение должно запросить соответствующий backlog. Если в Nginx или PHP-FPM осталось 128/511, фактическая очередь не станет 4096 только из-за sysctl.

Вторая ошибка — смотреть среднюю нагрузку за минуту и не видеть коротких пиков. Backlog может переполняться на 1–3 секунды, а средний CPU при этом будет 30%. Для таких случаев полезны частые замеры ss, экспорт метрик в мониторинг и анализ p99, а не только p50.

Третья ошибка — лечить backlog там, где проблема в приложении. Если PHP-запрос выполняется 5 секунд из-за медленного SQL, очередь будет расти при любом разумном backlog. Сначала уменьшите время удержания воркера, включите кеширование, проверьте индексы и внешние API.

Четвёртая ошибка — игнорировать graceful reload. После изменения listen и backlog старые worker-процессы могут ещё некоторое время обслуживать соединения. Обычно systemctl reload nginx достаточно, но при сомнениях проверьте PID, время старта процессов и фактический Send-Q в ss.

Пятая ошибка — включать слишком агрессивные сетевые настройки из случайных «тюнинг-гайдов». Параметры TCP взаимосвязаны. Если одновременно менять backlog, буферы, TIME_WAIT, retransmit, keepalive и congestion control, потом трудно понять, что помогло, а что навредило. Меняйте один слой за раз и фиксируйте результат.

Мини-чеклист после изменений

  • sysctl net.core.somaxconn показывает ожидаемое значение.
  • sysctl net.ipv4.tcp_max_syn_backlog соответствует выбранному профилю.
  • ss -ltnp показывает нужный Send-Q для Nginx на 80/443.
  • ss -lxnp или ss -ltnp показывает нужный backlog для PHP-FPM.
  • ListenOverflows и ListenDrops не растут во время обычного пика.
  • В логах Nginx не растут 502/504, а p95/p99 не ухудшились.
  • PHP-FPM не проводит всё время на пределе pm.max_children.
  • Лимиты LimitNOFILE, worker_connections и число PHP-воркеров согласованы с размером VDS.

Итоги

tcp backlog — это не один параметр, а связка очередей и лимитов на разных уровнях. tcp_max_syn_backlog относится к syn queue, somaxconn ограничивает accept queue, Nginx задаёт свой nginx backlog в директиве listen, а PHP-FPM — свой php-fpm listen backlog в настройках пула.

Правильная настройка начинается с наблюдения. ss показывает текущую заполненность очередей, netstat и nstat помогают увидеть накопленные переполнения, а логи Nginx/PHP-FPM связывают сетевую картину с реальными ошибками приложения. После этого можно аккуратно поднять лимиты ядра и backlog сервисов, не забывая про файловые дескрипторы, PHP-воркеры и время ответа backend.

Для большинства VDS с веб-нагрузкой хороший старт — somaxconn=4096, tcp_max_syn_backlog=8192, согласованный backlog в Nginx и PHP-FPM, включённые SYN cookies и регулярная проверка счётчиков переполнения. Дальше решение должно опираться на ваши метрики, а не на чужую универсальную шпаргалку.

Поделиться статьей

Вам будет интересно

Nginx debug log на VDS: включаем отладку только для нужного IP OpenAI Статья написана AI (GPT 5)

Nginx debug log на VDS: включаем отладку только для нужного IP

Показываем, как на VDS включить подробный nginx debug log не для всего трафика, а только для вашего IP: проверить сборку Nginx, на ...
Netplan на Ubuntu VDS: статический IPv4/IPv6, default route, DNS и безопасное применение по SSH OpenAI Статья написана AI (GPT 5)

Netplan на Ubuntu VDS: статический IPv4/IPv6, default route, DNS и безопасное применение по SSH

Пошаговая инструкция по Netplan для Ubuntu VDS: как найти интерфейс, прописать static IP, IPv6, default route и DNS, проверить net ...
NFSv4 на Linux VDS: exports, idmapd, firewall и systemd automount OpenAI Статья написана AI (GPT 5)

NFSv4 на Linux VDS: exports, idmapd, firewall и systemd automount

Разберём, как поднять NFSv4 Linux на VDS для общих каталогов: подготовить сервер и клиентов, описать exports, настроить idmapd, от ...