Серверу з 1-2 ГБ оперативної пам’яті досить swap на 1-2 ГБ, з 4-8 ГБ на 2-4 ГБ, а vm.swappiness для сервера з базою даних варто знизити з 60 до 10. Swap не лікує нестачу пам’яті, він лише дає час. Коли пам’ять закінчується, OOM Killer вбиває не процес, який її з’їв, а найбільший: на нашому стенді PHP-запит забрав зайві 150 МБ, а ядро вбило MariaDB (212 МБ), бо сам воркер PHP важив лише 108 МБ.
Ця стаття про VPS або власний сервер, де пам’яттю керуєте ви. На віртуальному хостингу swap і OOM Killer налаштовує провайдер, а для кожного акаунта діють власні ліміти пам’яті, тож замість убитої бази ви побачите помилку 508 Resource Limit Is Reached або повідомлення Allowed memory size exhausted.
Як ми перевіряли
10 листопада 2026 року на тестовому стенді: віртуальна машина з ядром Linux 6.18 і 7,8 ГБ RAM без swap. Невеликий VPS ми змоделювали за допомогою memory cgroup з лімітом 320 МБ, у якій працювали MariaDB 10.11.14 (buffer pool 128 МБ, таблиця на 156 МБ), PHP-FPM 8.3.6 (4 воркери, WordPress 6.4.3) і nginx 1.24. Важкий запит моделював PHP-скрипт, що поступово забирає 150 МБ. Кожні 0,5 с ми запитували головну сторінку, а події OOM брали з dmesg і лічильника oom_kill у cgroup. Swap на спільній машині ми не вмикали: команди перевірено на синтаксис, вплив swap на швидкість не вимірювали, zram описано за документацією. Документацію ядра, systemd і Red Hat перевірено того ж дня. Це поведінка тестового стенда, а не дані з серверів RichHost.
Чому OOM Killer вбиває базу, а не винуватця
Після прогріву MariaDB займала 217 МБ резидентної пам’яті (RSS), кожен воркер WordPress від 38 до 46 МБ. Потім один запит почав виділяти пам’ять шматками по 1 МБ, як це робить важкий експорт чи обробка зображень. Ось що записало ядро (вивід скорочено):
[ 6004.066291] php-fpm8.3 invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=0, oom_score_adj=0 [ 6004.066411] memory: usage 327680kB, limit 327680kB, failcnt 18064 [ pid ] uid tgid total_vm rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name [ 4307] 33 4307 94991 27764 20764 2110 4890 507904 0 0 php-fpm8.3 [ 6440] 0 6440 342132 54366 48160 6206 0 622592 0 0 mariadbd Memory cgroup out of memory: Killed process 6440 (mariadbd) total-vm:1368528kB, anon-rss:192640kB, file-rss:24824kB, shmem-rss:0kB, UID:0 pgtables:608kB oom_score_adj:0
Пам’ять попросив воркер PHP (pid 4307), а вбито MariaDB. Ядро не шукає винного. Для кожного процесу воно рахує бал: резидентна пам’ять, сторінки у swap і таблиці сторінок, плюс поправка oom_score_adj. Гине процес з найбільшим балом. У стовпці rss значення в сторінках по 4 КБ: 54 366 сторінок бази це 212 МБ, 27 764 сторінки воркера це 108 МБ. Сам PHP-запит після цього спокійно завершився з кодом 200. Ми повторили тест двічі, і обидва рази загинула база.
Із того самого звіту видно, що OOM Killer справді останній крок: перед ним ядро 18 064 рази впиралося в ліміт і витіснило все, що могло. Файловий кеш скоротився зі 159 МБ до 25 МБ, лишилася тільки спільна пам’ять OPcache.
Що побачили відвідувачі: з 60 запитів до головної сторінки один отримав 500 «Error establishing a database connection». На стенді наглядач mysqld_safe підняв базу в ту саму секунду. У systemd-юніті MariaDB в Ubuntu 24.04 стоять Restart=on-abnormal і RestartSec=5s, тож на реальному сервері вікно щонайменше п’ять секунд. Далі база працює з холодним кешем: після перезапуску вона займала 134 МБ замість 217 МБ, і перші запити йшли з диска. Якщо в момент вбивства йшов запис, InnoDB ще проходить відновлення після збою.
Як знайти сліди OOM Killer на своєму сервері
dmesg -T | grep -iE 'killed process|out of memory' journalctl -k --since "2 days ago" | grep -i oom grep -i 'killed process' /var/log/kern.log systemctl status mariadb
Рядок «Out of memory: Killed process» означає, що закінчилася пам’ять усього сервера. «Memory cgroup out of memory» означає, що спрацював ліміт cgroup: так буває з лімітами служб systemd і в контейнерах. Логіка вибору жертви в обох випадках однакова. Якщо база «сама» перезапускається вночі, а в її журналі помилок причини немає, першим перевірте dmesg. Повний порядок дій при збоях є в статті про діагностику проблем хостингу.
Як захистити MariaDB: oom_score_adj і systemd
Бал зсуває параметр oom_score_adj від -1000 до 1000. Ядро додає до балу частку всієї пам’яті: +500 означає плюс половину RAM (у cgroup плюс половину ліміту), -600 означає мінус 60 %, а -1000 повністю прибирає процес зі списку кандидатів. Поточні значення:
cat /proc/$(pidof mariadbd)/oom_score cat /proc/$(pidof mariadbd)/oom_score_adj choom -p $(pidof mariadbd)
Порівнюйте oom_score між процесами, а не з нулем: на сучасних ядрах процес майже без пам’яті має близько 666. На стенді MariaDB мала 684, воркери WordPress 669-670.
Пакет MariaDB в Ubuntu 24.04 уже містить підказку: у файлі /lib/systemd/system/mariadb.service є закоментований рядок OOMScoreAdjust=-600 і коментар розробників про те, що ядро охоче вбиває mariadbd, бо він великий. Тобто за замовчуванням захисту немає. Увімкніть його через override:
sudo systemctl edit mariadb [Service] OOMScoreAdjust=-600 sudo systemctl edit php8.3-fpm [Service] OOMScoreAdjust=500 sudo systemctl restart mariadb php8.3-fpm
Для MySQL служба зазвичай називається mysql. Значення -1000 робить базу недоторканною, і тоді за витік пам’яті в самій базі заплатять усі інші процеси, зокрема sshd, тож можна втратити доступ до сервера. Тому ми радимо для бази від -600 до -900, а для PHP-FPM позитивне значення: менеджер пулу просто запустить новий воркер замість убитого.
На стенді знизити oom_score_adj не вдалося: у контейнері немає capability CAP_SYS_RESOURCE, і запис -1000 повернув «Permission denied» (на звичайному VPS під root і через systemd це працює). Тому ми перевірили зворотний бік: дали процесам PHP-FPM +500. Той самий запит на 150 МБ:
Memory cgroup out of memory: Killed process 4307 (php-fpm8.3) total-vm:371772kB, anon-rss:80732kB, file-rss:8440kB, shmem-rss:19560kB, UID:33 pgtables:480kB oom_score_adj:500 WARNING: [pool cw18] child 4307 exited on signal 9 (SIGKILL) after 264.781050 seconds from start NOTICE: [pool cw18] child 8588 started
Цього разу важкий запит отримав 502, у журналі nginx з’явився рядок recv() failed (104: Connection reset by peer), а база з прогрітим кешем навіть не помітила події: усі 60 запитів до головної сторінки отримали 200. Арифметика проста: до 108 МБ воркера ядро додало половину ліміту (160 МБ), і 268 МБ переважили 212 МБ бази.

Перша лінія захисту: memory_limit і pm.max_children
Найкраще, щоб до OOM Killer не доходило взагалі. Той самий запит з memory_limit = 128M у пулі PHP-FPM завершився звичайною фатальною помилкою PHP за 0,35 с:
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2097152 bytes) in /srv/cw18/www/hog.php on line 4
Один запит з кодом 500, жодного вбитого процесу, лічильник oom_kill не змінився. Тому ставте memory_limit стільки, скільки справді потрібно сайту (для WordPress зазвичай 128-256 МБ), а не -1 чи 1024M про запас.
Друга половина рівняння: скільки воркерів працює одночасно. pm.max_children множить пам’ять одного воркера, і саме тут найчастіше помиляються, бо рахують RSS з top.
RSS чи PSS: скільки насправді важить воркер PHP-FPM
smem -P php-fpm -c "pid command uss pss rss" PID Command USS PSS RSS 4307 php-fpm: pool cw18 3364 9948 38016 4309 php-fpm: pool cw18 4828 12067 41632 4306 php-fpm: pool cw18 5856 13039 42596 4308 php-fpm: pool cw18 9116 16456 46128
RSS кожного воркера від 38 до 46 МБ, разом 164 МБ. Але більшість цих сторінок спільні: кеш OPcache і бібліотеки, які кожен воркер записує на свій рахунок. PSS ділить спільні сторінки порівну між процесами, і сума PSS (50 МБ) показує реальне споживання пулу. USS (3-9 МБ) звільниться, якщо завершити конкретний воркер. Якщо smem не встановлено, PSS видно у файлі /proc/PID/smaps_rollup.
Практичне правило: pm.max_children дорівнює пам’яті, яку ви готові віддати PHP, поділеній на середній PSS воркера в пікову годину з запасом на важкі запити. Пам’ять для PHP: RAM мінус buffer pool бази, мінус інші служби і мінус 15-20 % на файловий кеш. Розмір buffer pool підбирайте за обсягом даних, про це в статті про оптимізацію баз даних. Кожна версія PHP має власний пул зі своїми налаштуваннями, тож після зміни версії PHP перевірте pm.max_children у новому пулі.
Скільки swap потрібно серверу
Swap на вебсервері не робоча пам’ять, а буфер. Туди ядро виносить сторінки, яких давно ніхто не торкався, і він дає кілька хвилин, щоб помітити витік до приходу OOM Killer. Якщо сервер живе у swap постійно, сайт гальмує, і рішення одне: більше RAM або менше процесів.
| RAM сервера | Red Hat (RHEL 9) | Наша порада для VPS із сайтами |
|---|---|---|
| до 2 ГБ | 2 × RAM | 1-2 ГБ |
| 2-8 ГБ | дорівнює RAM | 2 ГБ |
| 8-64 ГБ | щонайменше 4 ГБ | 2-4 ГБ |
| понад 64 ГБ | залежно від навантаження, щонайменше 4 ГБ | 4 ГБ |
Таблиця Red Hat розрахована на будь-які системи, тому для сервера з базою ми радимо менше: великий swap лише відкладає OOM Killer, а сервер тим часом повільно перебирає сторінки з диска. Гібернація на сервері не потрібна, тож її колонку ми не враховуємо.
Як створити swap-файл
sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab swapon --show free -h
На стенді ми виконали перші три команди для файлу на 512 МБ (mkswap відповів Setting up swapspace version 1, size = 512 MiB) і свідомо не запускали swapon: машина спільна для кількох тестів, а swap діє на весь сервер. Тому вимірів швидкості зі swap у статті немає, лише перевірений синтаксис. На Btrfs і деяких старих системах fallocate для swap не підходить, тоді файл створюють командою dd if=/dev/zero of=/swapfile bs=1M count=2048. Якщо ви тільки готуєте сервер, swap зручно додати разом з іншими кроками з чекліста налаштування VPS на Ubuntu 24.04.
swappiness: що означає це число
За документацією ядра vm.swappiness задає відносну ціну swap порівняно зі скиданням файлового кешу: від 0 до 200, за замовчуванням 60. На машині стенда cat /proc/sys/vm/swappiness теж показав 60.
- 0: ядро не починає свопити, доки вільних і файлових сторінок не стане менше за верхню межу. Swap не вимкнено, він стає останнім резервом.
- 10: наша порада для сервера з базою. Ядро спершу звільняє файловий кеш, а пам’ять MariaDB і PHP лишає в RAM.
- 60: значення за замовчуванням, swap дорожчий за кеш, але використовується охоче.
- 100: swap і файловий кеш однаково дорогі.
- понад 100: для swap у пам’яті (zram, zswap), документація наводить приклад 133.
cat /proc/sys/vm/swappiness sudo sysctl vm.swappiness=10 echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
Команда sysctl діє до перезавантаження, файл у /etc/sysctl.d закріплює значення. На стенді ми їх не виконували, бо це глобальне налаштування спільної машини.
zram замість swap на диску
zram створює в оперативній пам’яті блоковий пристрій зі стисненням, і swap на ньому працює без звернень до диска. Документація ядра розраховує на стиснення приблизно 2:1 і вважає zram, більший за подвійний обсяг RAM, марним. Для VPS з 1-2 ГБ це розумний варіант: в Ubuntu його вмикають пакетом zram-tools або systemd-zram-generator, а swappiness для такого swap ставлять вище 100. Ми zram не тестували, бо в ядрі стенда немає цього модуля.
Пастки підрахунку пам’яті
Вивід free -h на машині стенда:
total used free shared buff/cache available Mem: 7.8Gi 2.4Gi 1.8Gi 112Mi 4.2Gi 5.4Gi Swap: 0B 0B 0B
Колонка free (1,8 ГБ) показує пам’ять, яка зовсім нічим не зайнята. Колонка available (5,4 ГБ) це оцінка ядра, скільки можна віддати новим процесам без swap з урахуванням кешу, який можна скинути. Орієнтуйтеся на available: мале free при великому buff/cache означає нормальну роботу Linux, а не нестачу пам’яті.
Ми перевірили це окремо: у cgroup з лімітом 128 МБ двічі прочитали файл на 600 МБ. Кеш заповнив увесь ліміт (127 МБ), ядро 5113 разів упиралося в межу і витісняло старі сторінки, але лічильник oom_kill лишився нульовим. Файловий кеш не викликає OOM Killer.
Третя пастка описана вище: сума RSS з top або ps для пулу PHP-FPM завищує споживання втричі (164 МБ проти 50 МБ PSS на стенді).
Симптом, команда, рішення
| Симптом | Що показує команда | Що робити |
|---|---|---|
| База перезапускається сама, у її журналі причини немає | dmesg -T | grep -i 'killed process' показує mariadbd |
OOMScoreAdjust для бази, менші pm.max_children і memory_limit |
| Періодичні 502 на важких сторінках | у dmesg убито php-fpm, у журналі PHP-FPM «exited on signal 9» | Знайдіть важкий скрипт, обмежте memory_limit, додайте RAM |
| Помилка 500 «Allowed memory size exhausted» | журнал помилок PHP | Оптимізуйте скрипт або помірно підніміть memory_limit |
| Сайт гальмує, диск постійно зайнятий | vmstat 1: стовпці si і so постійно більші за нуль |
Бракує RAM: менше воркерів, менший buffer pool або VPS з більшою пам’яттю |
| free показує лише 100-200 МБ вільних | available і buff/cache великі | Нічого не робіть, це кеш |
| Swap зайнятий, але сайт швидкий | vmstat 1: si і so нульові |
Норма: там лежать неактивні сторінки |
| Swap немає, OOM на кожному піку | swapon --show нічого не виводить |
Swap-файл на 1-2 ГБ і swappiness 10 |
Якщо вам не хочеться самим стежити за swap, OOM Killer і пулами PHP-FPM, сайт на WordPress чи іншій CMS можна розмістити на віртуальному хостингу: на тарифах RichHost сервер, PHP і бази даних налаштовує команда хостингу, а вам лишаються лише ліміти свого акаунта.
Часті питання
Чи можна зовсім вимкнути swap на сервері?
Можна, але тоді ядру нікуди винести навіть неактивні сторінки, і будь-який пік закінчується вбивством процесу, як у нашому тесті без swap. Невеликий swap з низьким swappiness обходиться дешевше за аварійний перезапуск бази.
Скільки оперативної пам’яті потрібно VPS під WordPress?
Порахуйте: buffer pool бази плюс PSS воркера, помножений на pm.max_children, плюс орієнтовно 300-500 МБ на систему і файловий кеш. На стенді чистий WordPress мав PSS 10-16 МБ на воркер, сайти з важкими плагінами важать більше, тому міряйте свій. Якщо пам’яті бракує постійно, порівняйте, скільки коштує VPS з більшим обсягом RAM.
Чому OOM Killer не вбив процес, який з’їв пам’ять?
Він не шукає винного, а вибирає процес з найбільшим балом: резидентна пам’ять, swap і таблиці сторінок плюс oom_score_adj. Велика база майже завжди важча за один воркер PHP.
Чи буває OOM Killer на віртуальному хостингу?
Пам’яттю сервера там керує провайдер, а сайт упирається в ліміти свого акаунта: memory_limit PHP, кількість процесів, помилку 508. Налаштовувати swap, swappiness чи oom_score_adj на віртуальному хостингу не потрібно, та й неможливо.



