Если вы хоть раз ловили внезапный пад сайта или крона из‑за нехватки ОЗУ на VDS, значит сработал классический oom killer ядра: он приходит в последний момент и безжалостно завершает «самый прожорливый» процесс. Сегодня в арсенале Linux есть более аккуратный инструмент — systemd-oomd. Он опирается на PSI (Pressure Stall Information) и cgroup v2, чтобы увидеть растущий «затвор» по памяти заранее и мягко освободить ресурсы, жертвуя наименее важными рабочими сервисами, а не базами данных и nginx.
OOM killer vs systemd-oomd: в чём разница
Классический OOM killer активируется, когда ядро уже не может удовлетворить запрос на выделение памяти. Он анализирует процессы по «стоимости» (badness) и убивает кого‑то быстро и без прелюдий. Это спасает хост, но часто ломает бизнес‑критичные штуки — БД, кеши, веб‑фронт.
systemd-oomd работает иначе:
- следит за PSI (pressure) по памяти — сколько времени задачи простаивают, ожидая освобождения RAM;
- применяет политики на уровне cgroup v2 (unit/slice), учитывая приоритеты сервисов;
- вмешивается раньше ядра: ограничивает, стопорит или завершает менее важные единицы, чтобы защитить «ядро» приложения;
- использует настройки
MemoryMax=,MemoryLow=,MemoryMin=,ManagedOOMPreference=для выбора «жертв».
Идея простая: вместо случайного «минус кто‑то один» получаем управляемое «минус что‑то второстепенное» — очереди, воркеры, фоновые консьюмеры, а не база или фронтенд.
PSI и «давление по памяти»: что смотреть
PSI показывает, сколько времени система теряет в ожидании освобождения памяти. Метрика доступна в /proc/pressure/memory и делится на some и full:
some— часть задач ждёт память;full— практически всё «стоит», это уже плохо.
cat /proc/pressure/memory
Чем выше средние значения за 10s/60s/300s, тем сильнее заторы. systemd-oomd использует пороги и длительность окна, чтобы среагировать до «чёрного экрана».

Предпосылки и совместимость
- cgroup v2 должен быть включён (современные Debian/Ubuntu, AlmaLinux/RHEL включают по умолчанию).
- Нужны сборки systemd с поддержкой oomd (везде, где systemd 247+).
- Память/свап учёт включён (по умолчанию на новых дистрибутивах).
Проверка и запуск systemd-oomd
Проверим статус и журналы:
systemctl status systemd-oomd
journalctl -u systemd-oomd -b
Если сервис не активен, включите и запустите:
systemctl enable --now systemd-oomd
Базовые пороги: oomd.conf
Файл настроек /etc/systemd/oomd.conf позволяет задать общие пороги срабатывания по PSI и использованию свопа. Пример разумных стартовых значений для VDS с небольшим запасом ОЗУ:
# /etc/systemd/oomd.conf
[OOM]
# Порог memory pressure (PSI) в процентах, при котором начинаем действовать
DefaultMemoryPressureLimitPercent=60
# Минимальная длительность превышения порога (гистерезис)
DefaultMemoryPressureDurationSec=45s
# При почти полном свопе действуем решительнее
DefaultSwapUsedLimitPercent=95
После изменения перезапустите сервис:
systemctl restart systemd-oomd
Эти параметры заставляют oomd реагировать до падения ядра, когда задержки по памяти стали систематическими.
Расстановка приоритетов: MemoryMax, MemoryLow/Min, ManagedOOMPreference
Глобальные пороги — это только половина дела. Настоящая магия — в расстановке приоритетов внутри вашего стека, чтобы «мягко» освобождать ресурсы.
Ограничиваем прожорливых: MemoryMax
MemoryMax= жёстко ограничивает память юнита (cgroup). Пример drop-in для фонового воркера:
# systemctl edit my-worker.service
[Service]
MemoryAccounting=yes
MemoryMax=1G
С MemoryMax= воркер не сможет вытеснить из памяти критичные сервисы.
Защищаем критичное: MemoryLow и MemoryMin
MemoryLow= — мягкая защита: oomd старается не отбирать память ниже указанного объёма. MemoryMin= — жёсткая гарантия (на cgroup v2), ниже которой вытеснение не производится. Для веб‑фронта и БД это must‑have.
# systemctl edit nginx.service
[Service]
MemoryAccounting=yes
# Гарантируем nginx минимум 256 МБ под активные воркеры и кэш
MemoryLow=256M
# systemctl edit postgresql.service
[Service]
MemoryAccounting=yes
# Не даём никому вытеснить БД ниже безопасного минимума (например, 2 ГБ)
MemoryMin=2G
Подбирайте величины из профиля нагрузки и headroom VDS. Начинайте с консервативных значений и наблюдайте.
Кого «попросить выйти» первым: ManagedOOMPreference
ManagedOOMPreference= подсказывает oomd, кого он должен стараться избегать, а кого можно завершить первым при давлении. Для критичных сервисов — «избегать», для фоновых — «можно убивать».
# systemctl edit php-fpm.service
[Service]
ManagedOOMPreference=avoid
# systemctl edit queue-consumer.service
[Service]
ManagedOOMPreference=kill
Значения поддерживаются в новых версиях systemd. Проверьте свойства юнита:
systemctl show -p ManagedOOMPreference -p MemoryLow -p MemoryMin -p MemoryMax php-fpm.service
Работа со slice: user.slice и system.slice
Часто стоит ограничить интерактивные и пользовательские процессы, чтобы они не мешали продакшен‑сервисам:
# systemctl edit user.slice
[Slice]
MemoryAccounting=yes
MemoryMax=512M
ManagedOOMPreference=kill
А вот system.slice и ваши приложение‑слайсы можно защитить:
# systemctl edit system.slice
[Slice]
MemoryAccounting=yes
MemoryLow=512M
Структурируйте сервисы по слайсам: фронт, бэк, БД, воркеры — так oomd будет принимать решения на уровне групп, а не отдельных процессов. Подробно про разбиение на слайсы для PHP‑FPM и не только — в статье Организация cgroup‑срезов для сервисов.
Наблюдение и диагностика
- Текущие значения PSI:
cat /proc/pressure/memory. - Потребление и лимиты юнита:
systemctl status,systemctl show -p MemoryCurrent -p MemoryMax <unit>. - Дерево cgroup:
systemd-cgls. - Топ по cgroup:
systemd-cgtop. - Журнал решений oomd:
journalctl -u systemd-oomd -b.
Когда oomd действует, вы увидите в журнале, какие cgroup попали под раздачу, с какими метриками PSI и свопа.
Тестовый прогон: проверяем политику
Создадим управляемое давление и посмотрим, кого «выгонят» первым. Встаньте на стенд или staging.
apt-get update
apt-get install -y stress-ng
Запустите потребление памяти в отдельном unit, чтобы видеть поведение:
systemd-run --unit=mem-hog --property=MemoryAccounting=yes --property=ManagedOOMPreference=kill stress-ng --vm 2 --vm-bytes 70% --timeout 120s
Следим за журналом и PSI:
journalctl -u systemd-oomd -f
watch -n 1 cat /proc/pressure/memory
Если всё настроено корректно, oomd сначала завершит низкоприоритетные единицы (ManagedOOMPreference=kill) или ограниченные MemoryMax=, сохранив в живых критичные MemoryLow/Min.
zram: мягкая подушка перед жёстким OOM
Сжатый своп в RAM (zram) часто улучшает ситуацию на небольших VDS. Он не «добавляет» физической памяти, но позволяет пережить кратковременные пики с минимальными задержками.
Установка и базовая настройка zram-tools
apt-get update
apt-get install -y zram-tools
Файл настроек по умолчанию: /etc/default/zramswap. Пример:
# /etc/default/zramswap
ALGO=zstd
PERCENT=50
PRIORITY=100
Перезапустите службу:
systemctl restart zramswap
Проверьте:
swapon --show
free -h
Для эффективности zram можно чуть поднять vm.swappiness и снизить порог «сжигания» pagecache:
sysctl vm.swappiness=80
sysctl vm.vfs_cache_pressure=50
Закрепите значения в отдельном файле:
printf "vm.swappiness = 80\nvm.vfs_cache_pressure = 50\n" > /etc/sysctl.d/99-zram.conf
sysctl --system
Свяжите это с oomd: когда DefaultSwapUsedLimitPercent близко к 100% и PSI высок, oomd примет меры ещё до того, как ядро начнёт массово отстреливать процессы.

Паттерны настройки для типичного веб‑стека
Nginx
MemoryLow=128–256M, чтобы не гасить фронт под пиковой нагрузкой.ManagedOOMPreference=avoid.
PHP-FPM или другой приложение‑рантайм
MemoryMax=на пул или сервис (например, 1–2G), с учётом количества воркеров.ManagedOOMPreference=avoidдля основного пула, а для второстепенных —kill.
Очереди/воркеры
MemoryMax=жёстко и консервативно.ManagedOOMPreference=kill, чтобы при давлении их закрывали первыми. Подробнее о запуске воркеров под systemd — в материале Контроль очередей и воркеров под systemd.
База данных
MemoryMin=достаточно высокий, чтобы кэш не проседал до смертельных значений.- Разумные настройки собственной памяти БД (shared_buffers, innodb_buffer_pool_size) под общий бюджет VDS.
Отладка инцидентов: что смотреть после срабатывания
- Журнал
systemd-oomd— какие unit/slice были затронуты и почему. - События ядра в
journalctl -k— не произошло ли классического OOM одновременно. - Потребление памяти по времени в мониторинге — как быстро росло давление.
- Параметры unit:
systemctl show -p MemoryCurrent -p MemoryLow -p MemoryMin -p MemoryMax <unit>. - Стек‑трейсы приложений — почему росла память (утечки, бурст загрузки, GC).
Контейнеры и Kubernetes
В контейнерах oomd тоже полезен, если вы запускаете их как systemd units. Однако в оркестраторах приоритеты лучше выражать через лимиты/requests и политики эвикции. На одиночных VDS с Docker/Podman без оркестратора удобно использовать systemd unit‑обёртки для контейнеров и те же MemoryMax, ManagedOOMPreference.
Чек‑лист внедрения на VDS
- Включите
systemd-oomd, задайте пороги в/etc/systemd/oomd.conf. - Разнесите сервисы по слайсам и выставьте бюджеты
MemoryMax. - Для критичных —
MemoryLow/Min, для второстепенных —ManagedOOMPreference=kill. - Подключите
zram, настройтеvm.swappiness. - Проведите нагрузочный тест с
stress-ng, посмотрите журналы. - Доведите значения до баланса между устойчивостью и производительностью.
Ответы на частые вопросы
Если у меня включён swap на диске, нужен ли zram? На медленном диске — zram часто лучше: сжатие в RAM быстрее диска и даёт буфер. Но не заменяет планирование памяти, это лишь смягчение пиков.
Будет ли oomd убивать сервисы слишком агрессивно? Если завышены пороги PSI и свопа, вмешательство наступит поздно; если занижены — рано. Начните с умеренных значений и наблюдайте 1–2 недели.
Чем MemoryLow отличается от MemoryMin? MemoryLow — мягкая защита: oomd старается не опускаться ниже. MemoryMin — жёсткая гарантия на уровне cgroup v2, которая ограничивает вытеснение даже при сильном давлении.
Нужно ли настраивать что‑то на уровне ядра про OOM? Как правило, нет: oomd вмешивается раньше. Изредка полезно проверить vm.overcommit_memory и особенности вашего рантайма.
Итоги
systemd-oomd превращает хаос «последнего удара» от OOM killer в управляемую деградацию сервиса. С PSI вы видите приближение проблемы, а с MemoryMax, MemoryLow/Min и ManagedOOMPreference определяете, кто жизненно важен, а кого можно перезапустить или завершить. Добавьте к этому небольшую «подушку» в виде zram, и ваш VDS перестанет падать из‑за разового бурста памяти — вместо этого вы получите предсказуемое и контролируемое поведение.


