Тюнинг Nginx под 50 тысяч соединений: ревью конфига
50 тысяч TCP-соединений не означают, что достаточно поднять worker_connections. В конфиге Nginx нужно согласовать число воркеров, лимиты файловых дескрипторов, keep-alive, проксирование и настройки ядра.

При 50 тысячах одновременных соединений Nginx чаще упирается не в процессор, а в файловые дескрипторы и неверно посчитанные лимиты. Одна настройка worker_connections 50000 проблему не решает: проксируемый запрос занимает дескриптор клиента и отдельный дескриптор upstream, а долгие keep-alive-сессии съедают лимит даже при низком трафике.
Сначала определите, что именно считается соединением
Перед рефакторингом конфига зафиксируйте профиль нагрузки. «50 тысяч соединений» может означать 50 тысяч клиентов, которые держат HTTP keep-alive, 50 тысяч WebSocket-сессий или короткие запросы с высокой частотой. Для Nginx это три разных сценария.
- Статика. На один клиентский запрос обычно нужен один файловый дескриптор сокета, а нагрузка чаще уходит в сеть и файловую систему.
- Reverse proxy. У запроса есть клиентское соединение и соединение с upstream. Теоретический лимит по сокетам может потребоваться почти вдвое выше числа клиентов.
- WebSocket или SSE. Соединения живут долго, поэтому лимит определяется количеством открытых сессий, а не запросов в секунду.
Проверьте конфигурацию именно того бинарника, который работает в проде: nginx -V покажет версию и параметры сборки, а nginx -T — итоговый конфиг вместе с подключёнными файлами. Для примеров ниже ориентир — Nginx 1.26.x на Linux с systemd. Названия директив могут быть доступны и в других версиях, но поведение стоит сверять с документацией вашей сборки.
Лимит соединений — это бюджет файловых дескрипторов, а не обещание пропускной способности.
Что проверять на ревью конфига
1. Число воркеров и дескрипторов
worker_processes auto выбирает число рабочих процессов по доступным CPU. Это хороший исходный вариант, но не повод считать, что система автоматически выдержит 50 тысяч сокетов. Максимальное число соединений задаётся на один worker, а суммарная ёмкость приблизительно равна произведению числа воркеров на worker_connections.
Для прокси расчёт должен учитывать upstream-соединения и служебные дескрипторы. Например, восемь воркеров с лимитом 8192 дают теоретические 65 536 соединений, но 50 тысяч клиентов через proxy могут потребовать существенно больше файловых дескрипторов.
# nginx 1.26.x, контекст main
worker_processes auto;
worker_rlimit_nofile 200000;
events {
use epoll;
worker_connections 8192;
}
Число 200000 здесь не является универсальным рецептом. Оно должно быть не меньше реальной потребности и поддерживаться лимитом сервиса systemd, например LimitNOFILE. Если systemd разрешает процессу только 4096 дескрипторов, директива Nginx не поднимет ОС-лимит сама.
2. Приём новых соединений
epoll — механизм Linux для ожидания событий на большом числе файловых дескрипторов[1]. На Linux он обычно предпочтительнее старых универсальных механизмов, но при сборке Nginx стоит проверить, что модуль и поддержка действительно доступны.
multi_accept on заставляет worker принимать сразу несколько ожидающих соединений. Это может ускорить разбор всплеска, но при неравномерной нагрузке один worker способен забрать слишком много новых клиентов. Включайте директиву только после замеров; для стабильной очереди запросов безопаснее начать с поведения по умолчанию.
reuseport в директиве listen распределяет входящие соединения по отдельным сокетам workers. Это не универсальное ускорение: оно зависит от ядра Linux, характера трафика и количества workers. Не смешивайте его с другими изменениями в одном релизе, иначе будет трудно понять причину результата.
# Вариант для теста на Linux. Проверяйте эффект отдельно от остальных изменений.
events {
use epoll;
worker_connections 8192;
}
http {
server {
listen 443 ssl reuseport;
server_name example.com;
}
}
3. Keep-alive и таймауты
Слишком длинный keepalive_timeout превращает свободных клиентов в постоянный расход дескрипторов. Слишком короткий таймаут увеличивает число TCP-handshake и нагрузку на CPU. Начальное значение нужно выбирать по реальному профилю клиентов, а не по числу 50 000.
http {
keepalive_timeout 15s;
keepalive_requests 1000;
# Для соединений Nginx с upstream, если приложение поддерживает keep-alive.
upstream app_backend {
server 127.0.0.1:8080;
keepalive 64;
}
}
Параметр keepalive внутри upstream задаёт кэш неактивных соединений на worker, а не общий лимит для всего Nginx. Большое значение может уменьшить стоимость повторных подключений к приложению, но одновременно занять дескрипторы и сокеты на стороне backend.
Рефакторинг типичного конфига reverse proxy
На ревью часто встречается конфигурация, где для каждого запроса заново открывается соединение с приложением, клиентские таймауты не определены, а буферизация включена без понимания размера ответов. Такой конфиг может работать на тестовом трафике и начать накапливать очередь при медленном upstream.
http {
upstream app_backend {
server 127.0.0.1:8080;
keepalive 64;
}
server {
listen 80;
server_name example.com;
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 60s;
proxy_pass http://app_backend;
}
}
}
Отключение заголовка Connection в этом варианте позволяет Nginx использовать HTTP/1.1 keep-alive с upstream. Но для WebSocket этот фрагмент недостаточен: нужны отдельные заголовки Upgrade и специальные таймауты. Не переносите настройки обычного API в location для долгоживущих соединений.
| Решение | Что даёт | Цена и риск | Когда выбирать |
|---|---|---|---|
worker_processes auto | Использует доступные CPU без ручного подсчёта workers | Суммарные лимиты нужно проверять после запуска | Почти всегда как базовая настройка |
Большой worker_connections | Повышает теоретическую ёмкость worker | Не помогает без достаточного nofile; proxy расходует больше дескрипторов | После расчёта и проверки лимитов |
reuseport | Разносит приём соединений по сокетам workers | Результат зависит от ядра и распределения трафика | При измеряемом перекосе на accept и Linux-окружении |
| Увеличение upstream keep-alive | Снижает число повторных подключений к приложению | Занимает сокеты и может перегрузить backend | Когда backend поддерживает keep-alive и есть частые запросы |
Что измерять до и после изменений
Не сравнивайте только среднее время ответа. Для каждого варианта фиксируйте версию Nginx, число CPU, объём памяти, тип диска, размер ответа, число клиентов, долю keep-alive и долю запросов к upstream. Без этих условий цифры «стало быстрее на 20 процентов» не позволяют повторить тест.
- Снимите базовую конфигурацию командой
nginx -Tи сохраните вывод вместе сnginx -V. - Проверьте лимит процесса: фактическое значение
ulimit -nв окружении сервиса должно соответствовать расчёту. - Наблюдайте число сокетов через
ss -s, состояниеTIME_WAIT, ошибки accept и загрузку каждого worker. - Проведите нагрузочный тест с постепенным ростом соединений. Отдельно тестируйте короткие HTTP-запросы, keep-alive и долгоживущие сессии.
- Сравните p95 и p99 задержки, долю 502/504, число открытых файлов и загрузку CPU. Изменение оставляйте только после проверки под нагрузкой, близкой к рабочей.
Где такой тюнинг ломается
Слишком высокий лимит соединений
Большой worker_connections не резервирует память под все сокеты заранее, но позволяет процессу принять больше работы. Если backend, база данных или сетевой канал не справляются, Nginx лишь удержит больше запросов в очереди. Пользователь увидит более долгий ответ, а не большую производительность.
Ошибочный расчёт для proxy
При 50 тысячах клиентов и одном upstream-соединении на запрос могут понадобиться десятки тысяч дополнительных дескрипторов. Реальная картина зависит от повторного использования соединений, буферизации и времени жизни запросов. Поэтому рассчитывайте верхнюю границу, а затем проверяйте её по фактическому числу открытых файлов.
Долгие соединения
Для WebSocket, SSE и больших загрузок нельзя механически применять короткий proxy_read_timeout. Для каждого типа трафика нужен отдельный location и собственная политика таймаутов. Инвалидация соединений и балансировка WebSocket — отдельная тема, здесь важен только расход лимитов.
Изменение без безопасного отката
Перед reload запускайте nginx -t, сохраняйте предыдущую версию конфига и проверяйте доступность нескольких endpoint после reload. Если конфигурация управляется контейнером или systemd, меняйте лимиты в том же слое, где запускается процесс, иначе ручное значение будет потеряно при рестарте.
Что делать дальше
Начните не с worker_connections 50000, а с инвентаризации: сколько клиентов одновременно подключено, сколько upstream-соединений открывает один worker и какой лимит дескрипторов реально получил процесс. Затем проведите два отдельных теста — для коротких запросов и долгих сессий. В проде после релиза смотрите на p99, 502/504, число открытых файлов и распределение соединений по workers; именно эти показатели покажут, помог рефакторинг или только увеличил очередь.
[1] Файловый дескриптор — идентификатор, под которым процесс ОС обращается к сокету, файлу или другому ресурсу. Лимит таких дескрипторов действует отдельно для процесса и может ограничить Nginx раньше, чем значение в конфиге.
Саммаризатор: 50 страниц или созвон — в 5 главных тезисов.
Спросить за 15 ₽Источники: Nginx documentation: Core functionality, Nginx documentation: HTTP proxy module
Частые вопросы
Достаточно ли поставить worker_connections 50000 для 50 тысяч клиентов?
Сколько workers нужно для 50 тысяч соединений?
Нужно ли включать reuseport и multi_accept одновременно?
Почему Nginx получает ошибки, хотя worker_connections ещё не достигнут?
Что менять для WebSocket с десятками тысяч подключений?
Как понять, что после тюнинга стало лучше?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.