Акция Панель управления ispmanager для VDS — первый месяц бесплатно
до 31.07.2026 Подробнее
Выберите продукт

Linux TCP ephemeral ports: TIME_WAIT, NAT и ip_local_port_range без боли

Если исходящие TCP-соединения сыпятся с EADDRNOTAVAIL / Cannot assign requested address, часто исчерпан диапазон ephemeral ports: порты «застревают» в TIME_WAIT, а NAT/SNAT и conntrack усиливают эффект. Ниже — диагностика через ss/tcpdump и безопасные шаги тюнинга.
Linux TCP ephemeral ports: TIME_WAIT, NAT и ip_local_port_range без боли

Зачем вообще разбираться с ephemeral ports

Ephemeral ports (эфемерные порты) — это «временные» локальные порты, которые ядро Linux выделяет клиентской стороне TCP-соединения. Когда ваш сервер делает исходящие запросы (к API, в БД, к почтовому шлюзу, к прокси, в микросервисы), он открывает сокет вида local_ip:ephemeral_portremote_ip:remote_port.

Проблемы начинаются, когда соединений много и они часто создаются/закрываются, либо есть NAT/SNAT (локально через iptables/nftables или на внешнем шлюзе), либо приложение генерирует шквал коротких запросов без пула/keepalive. Типичный симптом — ошибка приложения: EADDRNOTAVAIL / Cannot assign requested address. Обычно это означает, что ядро не смогло выделить новый локальный порт (и/или подходящую комбинацию адреса/порта) для исходящего соединения.

Важно: речь не про «серверный порт» (80/443), а про исходящие клиентские подключения вашего сервера. Даже веб-сервер может упираться в ephemeral ports, если он активно ходит во внешние сервисы.

Как ядро выбирает локальный порт и почему он может закончиться

Linux выбирает порт из диапазона net.ipv4.ip_local_port_range. По умолчанию в большинстве дистрибутивов это что-то вроде 32768 60999 или близко к 49152 65535 — зависит от версии/настроек. Точный диапазон смотрим так:

sysctl net.ipv4.ip_local_port_range

Количество доступных портов — это размер диапазона. Например, 32768..60999 — около 28232 портов. Кажется много, но при высоком RPS на коротких соединениях вы легко «съедите» этот запас, особенно если порты долго не возвращаются в пул из-за TIME_WAIT или из-за особенностей NAT.

Ключевое ограничение: один и тот же локальный порт можно использовать повторно только если не конфликтует 4-тупл: (src_ip, src_port, dst_ip, dst_port). На практике конфликт чаще всего именно по src_port, потому что dst_ip:dst_port для ваших запросов часто одинаковый (например, все стучатся в один API-эндпоинт).

Если вы уперлись в лимиты из-за исходящих подключений и приложению нужно больше ресурсов, удобнее переносить такие нагрузки на VDS, где вы контролируете sysctl и сетевую обвязку без ограничений общего окружения.

Схема 4-тупла TCP и диапазона ephemeral ports на исходящих соединениях

TIME_WAIT: почему «закрыл соединение» не значит «порт свободен»

Состояние TIME_WAIT — нормальная часть TCP. Обычно в него попадает сторона, которая активно закрыла соединение (послала FIN первой). В TIME_WAIT сокет держится, чтобы дождаться возможных «запоздалых» сегментов в сети и не перепутать их с новым соединением, а также корректно завершить TCP-обмен, если финальные пакеты потерялись.

Пока сокет в TIME_WAIT, комбинация адрес/порт может быть недоступна для нового соединения (в зависимости от условий и настроек). Если у вас много коротких исходящих коннектов к одному и тому же адресу/порту, TIME_WAIT быстро становится главным «пожирателем» ephemeral ports.

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

ss -s

А список именно TIME_WAIT (TCP):

ss -tan state time-wait | head

Если TIME_WAIT — десятки/сотни тысяч, и параллельно растут ошибки EADDRNOTAVAIL, вы почти у цели.

Чем опасно «лечить» TIME_WAIT не разбираясь

TIME_WAIT — это не «мусор», который можно бездумно выключить. Агрессивное переиспользование портов может приводить к редким и трудноуловимым сетевым сбоям (ошибки запросов, странные ретраи, некорректные ответы при совпадениях потоков).

Надежный подход: уменьшить количество коротких соединений и правильно подобрать диапазоны/тайминги. Sysctl-тюнинг — второй шаг, а не первый.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Почему NAT/SNAT усугубляет исчерпание портов

Когда на пути есть NAT (часто в контейнерных сетях, Kubernetes, при выходе через общий шлюз, либо при SNAT на самом сервере), появляются дополнительные ограничения:

  • Портовая емкость ограничена на «внешней» стороне. Если много внутренних клиентов «выходят» через один публичный IP, все конкурируют за один пул портов.
  • conntrack хранит состояние соединений. Даже после завершения TCP запись может жить какое-то время и влиять на повторное использование портов, а также создавать нагрузку на таблицу.
  • Коллизии по 5-туплу в NAT-таблице. В NAT добавляется трансляция, и порт может быть занят не только локальным TCP-стеком, но и состояниями NAT.

Отдельный класс проблем — переполнение conntrack: это уже не только EADDRNOTAVAIL, но и дропы новых соединений на уровне firewall. На практике эти истории часто идут рядом: много коротких коннектов → много записей → много TIME_WAIT и нагрузка на conntrack.

Мини-диагностика conntrack и NAT-состояний

Проверяем текущую загрузку conntrack:

sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max

Если nf_conntrack_count близок к nf_conntrack_max, NAT/фильтрация начинают «сыпаться» первыми. Симптомы бывают разные: от таймаутов до нестабильного падения исходящих запросов.

Типичные симптомы: EADDRNOTAVAIL и Cannot assign requested address

Эти сообщения часто всплывают в логах приложений и прокси. На уровне POSIX это означает: не удалось привязать локальный адрес/порт для нового соединения. Самые частые причины:

  • исчерпан диапазон ephemeral ports из ip_local_port_range;
  • очень много TIME_WAIT на одном направлении (к одному dst_ip:dst_port);
  • проблемы с SNAT: много внутренних потоков через один внешний IP;
  • переполнение conntrack (в NAT-сценариях);
  • приложение делает слишком много соединений без keepalive/пула.

Критически важно: сначала понять, кто генерирует поток соединений (какой процесс) и куда (какой remote).

Практическая диагностика: ss и tcpdump

1) Сколько TIME_WAIT и какие состояния доминируют

Быстро оценить масштаб по TCP-состояниям:

ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -nr | head

Посмотреть топ назначений для TIME_WAIT (полезно, когда все упирается в один внешний API):

ss -tan state time-wait | awk 'NR>1 {print $5}' | sed 's/:.*//' | sort | uniq -c | sort -nr | head

Если «виновник» — один IP (или пара IP), дальше почти всегда помогает keepalive/пул соединений. Расширение диапазона портов — страховка, но не лечение первопричины.

2) Кто порождает соединения: поиск по PID

ss умеет показывать процессы:

ss -tanp | head

В продакшене вывод может быть большим, поэтому фильтруйте по направлению (например, по remote-порту):

ss -tanp '( dport = :443 )' | head

Если вы отлаживаете таймауты на стыке веб-сервер ↔ апстрим, полезно сверить картину с настройками шлюза. В этом контексте может пригодиться разбор таймаутов Nginx и PHP-FPM (gateway timeouts), чтобы не провоцировать лишние ретраи и churn соединений.

3) Подтвердить картину на уровне пакетов: tcpdump

Когда нужно убедиться, что действительно идет «шквал» коротких TCP-сессий (SYN → data → FIN/RST), снимите короткий трейс:

tcpdump -nn -i any 'tcp and (tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0)' -c 200

Дальше смотрим, много ли SYN/FIN, нет ли массовых RST, нет ли ретраев SYN (что уже похоже на сетевую проблему или фильтрацию).

Диагностика исходящих TCP-соединений через ss и tcpdump

ip_local_port_range: расширяем «емкость» правильно

Самое безопасное и предсказуемое действие — увеличить диапазон ephemeral ports. Обычно рекомендуют не залезать в низкие порты и оставить место под локальные сервисы, но расширить верхнюю границу до 65535.

Например (подберите диапазон под свою среду):

sysctl -w net.ipv4.ip_local_port_range='20000 65535'

Для постоянного применения — через конфиги sysctl в вашей ОС (обычно в /etc/sysctl.d/), затем:

sysctl --system

Что это дает: больше уникальных исходящих портов, а значит выше потолок по числу параллельных и/или частых соединений к одному назначению.

Что это не решает: если приложение продолжит создавать десятки тысяч новых TCP-сессий в секунду без reuse/keepalive, вы лишь отодвинете проблему.

TIME_WAIT и sysctl: tcp_tw_reuse и tcp_fin_timeout

Частый совет из интернета: «включить tcp_tw_reuse и уменьшить tcp_fin_timeout». В реальности это разные рычаги с разными рисками.

tcp_tw_reuse: осторожно и только понимая последствия

net.ipv4.tcp_tw_reuse исторически позволял переиспользовать сокеты в TIME_WAIT для новых исходящих соединений при определенных условиях. Это может уменьшить вероятность EADDRNOTAVAIL на клиентах с очень большим количеством коротких коннектов.

Но имейте в виду:

  • поведение зависит от версии ядра и особенностей сетевого стека;
  • в некоторых сценариях (нестабильные сети, асимметричная маршрутизация, NAT, балансировщики) агрессивный reuse повышает риск редких и странных ошибок;
  • это не замена нормальному connection pooling и keepalive.

Если вы рассматриваете эту настройку, делайте это после расширения ip_local_port_range и после исправления приложения, и обязательно проверяйте на стенде или на части трафика.

tcp_fin_timeout: не про TIME_WAIT, но влияет на «хвосты» закрытия

net.ipv4.tcp_fin_timeout — таймаут для состояний закрытия соединения (например, FIN_WAIT_2) в ряде ситуаций. Его часто путают с таймером TIME_WAIT. Уменьшение tcp_fin_timeout может помочь, если у вас зависают «полузакрытые» соединения из-за особенностей приложений/пиров, но это не магическая кнопка для уборки TIME_WAIT.

Проверить текущее значение:

sysctl net.ipv4.tcp_fin_timeout

Если вы видите много FIN_WAIT_2 и это подтверждено трассировками или логами, тогда точечное уменьшение может быть оправдано. Но сначала убедитесь, что проблема именно в этом состоянии, а не в TIME_WAIT.

NAT/SNAT: что делать, если упираетесь в портовую емкость

Если узкое место — общий SNAT (например, много контейнеров/подов или несколько внутренних сервисов «выходят» через один внешний IP), типовые варианты:

  • Добавить внешние адреса и распределить исходящие потоки. Больше внешних IP → больше суммарная емкость портов (диапазон умножается на количество адресов).
  • Разнести трафик по разным SNAT-адресам по сервисам/подсетям. Это снижает конкуренцию за порты.
  • Увеличить лимиты conntrack и пересмотреть таймауты. Это лечит «таблица переполнена», но не всегда лечит именно EADDRNOTAVAIL.
  • Снизить создание новых TCP-сессий на уровне приложений. HTTP keepalive, gRPC/HTTP2, пулы соединений к БД/кешу, адекватные таймауты и ретраи.

Главная мысль: при NAT вы упираетесь не только в локальный стек, но и в ресурс трансляции. Поэтому «просто увеличить ip_local_port_range» на одной машине может не помочь, если бутылочное горлышко — внешний SNAT.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Чек-лист: когда все падает прямо сейчас

  1. Подтвердить симптом: ошибки EADDRNOTAVAIL / Cannot assign requested address в логах приложения/прокси.

  2. Посмотреть масштаб по состояниям TCP: ss -s и долю TIME_WAIT.

  3. Понять направление: к каким remote IP/портам больше всего коннектов в TIME_WAIT.

  4. Найти процесс/сервис-источник через ss -tanp (по возможности фильтруя по dport).

  5. Если есть NAT: проверить nf_conntrack_count и nf_conntrack_max.

  6. Быстрый безопасный тюнинг: расширить ip_local_port_range (если диапазон небольшой).

  7. Долгосрочно: включить keepalive/pooling, уменьшить churn соединений, выровнять таймауты, при SNAT — добавить IP или разнести исходящий трафик.

Пара практических сценариев и что обычно помогает

Сценарий A: сервис делает много HTTPS-запросов к одному API

Признаки: огромный TIME_WAIT на dport 443 к одному IP/подсети; периодический EADDRNOTAVAIL при пиках.

  • Включить HTTP keepalive в клиенте, ограничить одновременные коннекты, настроить пул.
  • Увеличить ip_local_port_range.
  • Проверить, что ретраи не слишком агрессивные и не создают «бурст» соединений.

Сценарий B: много контейнеров выходят наружу через один SNAT

Признаки: проблема появляется «волнами» при нагрузке всего кластера; conntrack близок к максимуму; ошибки могут быть на разных сервисах одновременно.

  • Увеличить емкость conntrack и мониторить nf_conntrack_count.
  • Разнести SNAT по нескольким адресам (если возможно).
  • Сократить churn соединений (keepalive/HTTP2), иначе любые лимиты будут «съедены».

Сценарий C: много FIN_WAIT_2 и «подвисшие» закрытия

Признаки: в ss доминируют не TIME_WAIT, а состояния закрытия; часто связано с некорректными таймаутами приложений или с проблемным пэром.

  • Разобраться с таймаутами приложения и сервера-назначения.
  • Только после подтверждения — аккуратно настраивать net.ipv4.tcp_fin_timeout.

Что мониторить, чтобы не ловить это ночью

  • количество TCP-сокетов по состояниям (особенно TIME_WAIT);
  • ошибки приложений вида EADDRNOTAVAIL;
  • значение net.ipv4.ip_local_port_range как часть «паспортных данных» хоста;
  • net.netfilter.nf_conntrack_count и долю от net.netfilter.nf_conntrack_max (если есть NAT/фильтрация);
  • RPS на исходящие запросы и число одновременных коннектов в клиентах (пулы/агенты).

Если вы используете Nginx как TCP/UDP-прокси и видите похожие симптомы на сетевом стыке, может быть полезен материал про балансировку TCP/UDP через Nginx stream — там часто всплывают вопросы таймаутов и поведения соединений.

Итоги

Исчерпание ephemeral ports — это не «мистика ядра», а предсказуемый ресурсный потолок. В простых случаях его достаточно поднять расширением ip_local_port_range. В более сложных — проблему создают хвосты TIME_WAIT из-за коротких соединений или NAT/SNAT с conntrack, где узким местом становится общая портовая емкость на внешнем адресе.

Самый надежный путь: сначала диагностика через ss и, при необходимости, tcpdump; затем расширение диапазона портов; после — исправление модели соединений в приложениях (keepalive/pooling); и только потом аккуратный sysctl-тюнинг вроде tcp_tw_reuse и точечная настройка net.ipv4.tcp_fin_timeout при подтвержденной необходимости.

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

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

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...