Выберите продукт

Linux: Cannot fork / Resource temporarily unavailable — ulimit, cgroups v2 и pids.max

Cannot fork: Resource temporarily unavailable (EAGAIN) почти всегда означает исчерпание лимита процессов/потоков: ulimit -u (RLIMIT_NPROC), cgroups v2 pids.max или systemd TasksMax/LimitNPROC. Ниже — быстрые проверки, поиск виновника и безопасное повышение лимитов.
Linux: Cannot fork / Resource temporarily unavailable — ulimit, cgroups v2 и pids.max

Ошибка Cannot fork: Resource temporarily unavailable обычно означает, что процесс попытался создать дочерний процесс через fork()/clone(), но ядро вернуло EAGAIN. В продакшене это почти всегда не «кончилась память», а сработал лимит на количество задач (процессов и потоков) на одном из уровней: ulimit -u, systemd или cgroups.

Что означает Cannot fork (EAGAIN) и что именно считается «процессом»

В Linux под лимиты часто попадают не только «процессы» в бытовом смысле, но и потоки: в ядре это задачи, которым нужен PID/запись в таблице. Поэтому один сервис может иметь «мало процессов», но тысячи потоков и внезапно упереться в ограничение.

Отличие от проблем с памятью простое: при нехватке памяти чаще встречаются ENOMEM, OOM-killer и сообщения в dmesg про убийство процессов. При EAGAIN чаще виноваты политики/лимиты.

Где чаще всего срабатывает лимит: ulimit, systemd, cgroups v2

Ограничения на количество задач могут накладываться одновременно, и сработает самое «узкое» место:

  • RLIMIT_NPROC — то, что показывает ulimit -u (его часто называют nproc).
  • systemd — лимиты юнита: TasksMax (задачи в cgroup) и LimitNPROC (обёртка над RLIMIT_NPROC для процесса сервиса).
  • cgroups v2 — контроллер pids: pids.max (лимит задач в группе) и pids.current (текущая занятость).

Практическая цель диагностики: определить, какой именно уровень ограничил создание задач, затем найти «кто раздулся», и только после этого менять параметры.

Быстрая проверка лимитов: ulimit -u и systemd TasksMax/LimitNPROC

Быстрые проверки: подтвердить лимит и понять, где он стоит

1) Проверка RLIMIT_NPROC (ulimit -u) в текущей сессии

Если ошибка проявляется в интерактивной SSH-сессии (у конкретного пользователя не запускаются команды), начинайте с этого:

ulimit -u
ulimit -a | grep -E 'processes|max user processes'

Если лимит вроде 1024 или 4096, а у вас воркеры/очереди/Java/CI или много параллелизма — это частая причина.

2) Проверка systemd-лимитов для сервиса

Если ломается только один демон под systemd, смотрим его ограничения:

systemctl show -p TasksMax -p TasksCurrent -p LimitNPROC your-service.service
systemctl status your-service.service

TasksMax ограничивает количество задач (процессы+потоки) в cgroup юнита. LimitNPROC ограничивает число процессов для UID (через RLIMIT) в контексте сервиса.

3) Проверка cgroups v2: pids.max и pids.current

Сначала убедитесь, что у вас unified hierarchy (cgroups v2):

stat -fc %T /sys/fs/cgroup

Если видите cgroup2fs, это v2. Дальше узнайте cgroup сервиса и посмотрите лимиты:

systemctl show -p ControlGroup your-service.service

Допустим, вернулось /system.slice/your-service.service. Тогда:

CG=/sys/fs/cgroup/system.slice/your-service.service
cat $CG/pids.max
cat $CG/pids.current

Если pids.current близко к pids.max, причина практически подтверждена.

Как отличить ulimit -u от pids.max и TasksMax: типичные признаки

  • Проблема у конкретного пользователя: в его shell не стартуют даже простые команды. Часто виноват ulimit -u и настройки PAM limits.
  • Проблема только у одного systemd-сервиса: система в целом жива, но воркеры не создаются. Чаще всего это TasksMax, LimitNPROC или pids.max его cgroup.
  • Проблема в контейнере: почти всегда это лимит pids на уровне cgroup контейнера или TasksMax для scope/юнита контейнера на хосте.

Кто «съел процессы»: практическая диагностика

Топ по числу процессов по пользователям

ps -eo user= | sort | uniq -c | sort -nr | head

Так вы быстро увидите UID, под которым расплодились процессы (часто это сервисный пользователь, CI-агент или веб-пользователь).

Топ по потокам (когда «процессов немного», но лимит всё равно сработал)

Многие лимиты считают tasks, то есть потоки тоже. Поэтому полезно сравнить:

ps -e --no-headers | wc -l
ps -eL --no-headers | wc -l
ps -eLo user= | sort | uniq -c | sort -nr | head

Если ps -e выглядит терпимо, а ps -eL показывает в разы больше — упёрлись в потоки или в общий лимит задач.

Сколько потоков у конкретного процесса

PID=1234
ls /proc/$PID/task | wc -l

Тысячи потоков — повод проверять настройки пулов, лимиты воркеров и утечки (особенно в Java, Node.js, PHP-FPM, пайплайнах CI).

Если это systemd-сервис: текущие tasks и лимит

systemctl show -p TasksCurrent -p TasksMax your-service.service

Если TasksCurrent близко к TasksMax, дальше бессмысленно «перезапускать до победы»: нужно уменьшать параллелизм или повышать лимит.

Если подозреваете зацикливание от cron/таймеров

Частая причина «внезапного» размножения процессов — ошибка в cron или systemd-timer. Если у вас есть такие задачи, держите под рукой разбор и диагностику: cron, crontab и systemd timers: как найти источник бесконечных запусков.

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

Частые причины исчерпания PID/tasks

  • Зацикленный скрипт/супервизор: бесконечный цикл запуска команды без паузы и без контроля завершения.
  • Слишком много воркеров: например, десятки инстансов template-юнитов или чрезмерный параллелизм в очередях.
  • Переоценённый веб-пул: завышенные workers/children в веб-сервере или PHP-FPM, плюс потоки внутри каждого воркера.
  • Ошибочная «fork-bomb»: рекурсия в shell/CI job, неверная обработка ошибок, отсутствие блокировок.
  • Налипание процессов: короткие процессы копятся, потому что что-то блокируется на I/O, DNS, файловых блокировках или очередях.

Критерий из практики: если число задач растёт ступеньками и почти не падает — вероятно, утечка или зацикливание. Если растёт строго вместе с нагрузкой — чаще неверный sizing и отсутствие backpressure.

Как безопасно увеличить лимиты (и где именно это делать)

Повышайте лимиты аккуратно: они защищают хост от полного отказа, когда «размножение» выходит из-под контроля. Хорошая последовательность: сначала остановить источник роста, затем подобрать разумные значения лимитов.

1) Временно поднять ulimit -u в текущей сессии

ulimit -u 16384

Это помогает для диагностики и кратковременной стабилизации, но не является постоянным решением.

2) Постоянно настроить nproc через PAM limits

Обычно это делается в /etc/security/limits.d/ (конкретный файл и приоритет зависят от дистрибутива):

# /etc/security/limits.d/90-nproc.conf
appuser soft nproc 16384
appuser hard nproc 32768

После изменения переподключите пользователя или перезапустите сервис, чтобы лимиты применились.

3) Поднять LimitNPROC для systemd-сервиса

Если сервис запускается systemd, правильнее фиксировать лимиты в override:

systemctl edit your-service.service

Содержимое drop-in:

[Service]
LimitNPROC=32768

Применение:

systemctl daemon-reload
systemctl restart your-service.service

4) Поднять TasksMax (и тем самым лимит tasks в cgroup юнита)

Если упираетесь именно в задачи (потоки тоже считаются), повышайте TasksMax:

systemctl edit your-service.service
[Service]
TasksMax=20000
systemctl daemon-reload
systemctl restart your-service.service

Значение TasksMax=infinity иногда применяют, но безопаснее поставить расчётный предел, чтобы сохранить предохранитель.

Когда имеет смысл трогать pids.max напрямую

Правка pids.max в /sys/fs/cgroup часто временная: после перезапуска юнита/контейнера её перезапишет systemd или runtime. В мире systemd предпочтительнее управлять через TasksMax (или настройки slice), а в контейнерах — через параметры запуска/оркестратора.

Диагностика источника роста задач: процессы, потоки и pids.current

Типовые сценарии: короткие планы действий

Сценарий A: Cannot fork в SSH-сессии конкретного пользователя

  1. Проверьте ulimit -u в этой сессии.
  2. Посчитайте процессы/потоки пользователя (агрегацией по user или ps -u appuser).
  3. Найдите источник (cron/скрипт/CI) и остановите его.
  4. Только после стабилизации фиксируйте постоянный лимит через PAM limits.

Сценарий B: падает только systemd-сервис

  1. Посмотрите TasksMax, TasksCurrent, LimitNPROC.
  2. Если упёрлись в TasksMax или pids.max — поднимайте TasksMax и параллельно снижайте лишний параллелизм в конфиге.
  3. Если упёрлись в LimitNPROC — поднимайте LimitNPROC и проверьте PAM limits, если сервис стартует от конкретного пользователя.

Если это сервис с воркерами (очереди, супервизор, template-юниты), полезно навести порядок в модели воркеров: как правильно управлять воркерами через supervisor/systemd и не устроить лавину процессов.

Сценарий C: ошибка внутри контейнера

Проверьте с хоста, какой лимит задач выставлен для scope/юнита контейнера и чему равны pids.current/pids.max в его cgroup. Дальше либо увеличивайте лимит на уровне runtime, либо уменьшайте параллелизм внутри контейнера (число воркеров, потоки, пул).

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

Почему «просто поднять лимит» иногда делает хуже

Лимит задач защищает систему от деградации. Если убрать предохранитель и не устранить причину размножения, вы рискуете получить рост load average, проблемы с запуском диагностических утилит (они тоже не смогут fork), а затем каскадные отказы сервисов.

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

Мини-чеклист на инцидент

  1. Определите область: весь хост, один пользователь или один сервис.
  2. Проверьте ulimit -u (для user-уровня) и LimitNPROC (для systemd-сервиса).
  3. Проверьте TasksMax, TasksCurrent и (в cgroups v2) pids.max/pids.current.
  4. Найдите виновника по процессам и потокам и остановите источник (скрипт, таймер, воркеры, неправильный пул).
  5. Зафиксируйте постоянные настройки через systemd override или PAM limits, а не ручной записью в /sys/fs/cgroup.
  6. Добавьте алерты: рост tasks по сервису/пользователю и предупреждение до достижения лимита.

Итог

Cannot fork: Resource temporarily unavailable и EAGAIN почти всегда означают упор в лимит задач: ulimit -u (RLIMIT_NPROC), systemd (TasksMax, LimitNPROC) или cgroups v2 (pids.max). Быстро определите уровень срабатывания, найдите «кто раздулся», стабилизируйте систему и только затем аккуратно повышайте лимит там, где это действительно нужно.

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

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

Linux: Too many links (EMLINK) на ext4/XFS — причины и рабочие обходы OpenAI Статья написана AI (GPT 5)

Linux: Too many links (EMLINK) на ext4/XFS — причины и рабочие обходы

EMLINK «Too many links» появляется внезапно: в CI при деплое, при rsync --link-dest, cp -al или распаковке архивов. Разберём лимит ...
Linux ENOSPC из-за inode: диагностика df -i и быстрые способы исправления OpenAI Статья написана AI (GPT 5)

Linux ENOSPC из-за inode: диагностика df -i и быстрые способы исправления

ENOSPC «No space left on device» появляется даже при свободных гигабайтах, если закончились inode. Разберём проверку df -i, поиск ...
systemd: journalctl, systemctl status и лимиты запуска StartLimit/Timeout* без боли OpenAI Статья написана AI (GPT 5)

systemd: journalctl, systemctl status и лимиты запуска StartLimit/Timeout* без боли

Если сервис в systemd уходит в restart loop, падает с failed to start или «не успевает» подняться — почти всегда дело в логах и ли ...