Přejít k obsahu
Pošta Kabinet Vybrat tarif

Swap a OOM killer: kolik swapu potřebujete a proč MySQL padá

Swap a OOM killer: kolik swapu potřebujete a proč MySQL padá

Server s 1-2 GB RAM potřebuje 1-2 GB swapu, server se 4-8 GB potřebuje 2-4 GB a na databázovém serveru se vyplatí snížit vm.swappiness z 60 na 10. Swap nedostatek paměti neléčí, jen získává čas. Když paměť dojde, OOM killer nezabije proces, který ji snědl, ale ten největší: na našem stendu si PHP požadavek vzal 150 MB navíc a jádro zabilo MariaDB (212 MB), protože samotný PHP worker vážil jen 108 MB.

Tento článek je o VPS nebo vlastním serveru, kde si paměť spravujete sami. Na sdíleném hostingu nastavuje swap a OOM killer poskytovatel, každý účet má vlastní paměťové limity, a místo zabité databáze uvidíte chybu 508 Resource Limit Is Reached nebo hlášení Allowed memory size exhausted.

Jak jsme testovali

10. listopadu 2026 na testovacím stendu: virtuální stroj s jádrem Linux 6.18 a 7,8 GB RAM, bez swapu. Malý VPS jsme napodobili paměťovou cgroup s limitem 320 MB, ve které běžely MariaDB 10.11.14 (buffer pool 128 MB, tabulka 156 MB), PHP-FPM 8.3.6 (4 workery, WordPress 6.4.3) a nginx 1.24. Roli těžkého požadavku hrál PHP skript, který postupně zabere 150 MB. Úvodní stránku jsme načítali každých 0,5 s a události OOM jsme brali z dmesg a z počítadla oom_kill v cgroup. Na sdíleném stroji jsme swap nezapnuli: syntaxi příkazů jsme ověřili, vliv swapu na rychlost jsme neměřili a zram popisujeme podle dokumentace. Dokumentaci jádra, systemd a Red Hat jsme ověřili týž den. Jde o chování testovacího stendu, ne o data ze serverů RichHost.

Proč OOM killer zabije databázi, a ne viníka

Po zahřátí zabírala MariaDB 217 MB rezidentní paměti (RSS), každý worker WordPressu 38 až 46 MB. Pak začal jeden požadavek alokovat paměť po blocích 1 MB, jako to dělá těžký export nebo změna velikosti obrázků. Tohle zapsalo jádro (zkráceno):

[ 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

O paměť požádal PHP worker (pid 4307) a zemřela MariaDB. Jádro viníka nehledá. Pro každý proces počítá skóre: rezidentní paměť, stránky ve swapu a tabulky stránek plus korekci oom_score_adj. Zemře proces s nejvyšším skóre. Sloupec rss je ve stránkách po 4 KB: 54 366 stránek databáze je 212 MB, 27 764 stránek workeru je 108 MB. Samotný PHP požadavek pak skončil se stavem 200. Test jsme zopakovali dvakrát a pokaždé zemřela databáze.

Stejný výpis ukazuje, že OOM killer je opravdu poslední krok: předtím jádro narazilo na limit 18 064krát a uvolnilo všechno, co šlo. Souborová cache se zmenšila ze 159 MB na 25 MB a zůstala v ní jen sdílená paměť OPcache.

Co viděli návštěvníci: jeden z 60 požadavků na úvodní stránku dostal chybu 500 “Error establishing a database connection“. Na stendu supervizor mysqld_safe databázi restartoval během téže sekundy. Jednotka systemd pro MariaDB v Ubuntu 24.04 má Restart=on-abnormal a RestartSec=5s, takže na skutečném serveru trvá výpadek nejméně pět sekund. Potom databáze běží se studenou cache: po restartu zabírala 134 MB místo 217 MB a první dotazy šly na disk. Pokud v okamžiku zabití probíhal zápis, InnoDB navíc provádí obnovu po havárii (crash recovery).

Jak najít stopy OOM killeru na svém serveru

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” znamená, že paměť došla celému serveru. “Memory cgroup out of memory” znamená, že byl dosažen limit cgroup, což se stává u limitů služeb systemd a v kontejnerech. Logika výběru oběti je v obou případech stejná. Pokud se databáze v noci restartuje “sama od sebe” a její chybový log neuvádí důvod, podívejte se nejdřív do dmesg. Celý postup hledání závad najdete v našem návodu na diagnostiku problémů s hostingem.

Jak ochránit MariaDB: oom_score_adj a systemd

Skóre se posouvá pomocí oom_score_adj, od -1000 do 1000. Jádro ke skóre přičte podíl celkové paměti: +500 znamená plus polovinu RAM (v cgroup plus polovinu limitu), -600 znamená minus 60 % a -1000 proces ze seznamu kandidátů úplně vyřadí. Aktuální hodnoty:

cat /proc/$(pidof mariadbd)/oom_score
cat /proc/$(pidof mariadbd)/oom_score_adj
choom -p $(pidof mariadbd)

Porovnávejte oom_score mezi procesy, ne s nulou: na současných jádrech má proces téměř bez paměti zhruba 666. Na stendu měla MariaDB 684, workery WordPressu 669-670.

Balíček MariaDB v Ubuntu 24.04 už obsahuje nápovědu: v /lib/systemd/system/mariadb.service je zakomentovaný řádek OOMScoreAdjust=-600 s komentářem vývojářů, že jádra rády zabíjejí mariadbd, protože je velký. Jinými slovy, ve výchozím stavu žádná ochrana není. Zapněte ji přes override:

sudo systemctl edit mariadb
[Service]
OOMScoreAdjust=-600

sudo systemctl edit php8.3-fpm
[Service]
OOMScoreAdjust=500

sudo systemctl restart mariadb php8.3-fpm

U MySQL se služba obvykle jmenuje mysql. Hodnota -1000 udělá z databáze nedotknutelný proces a únik paměti v samotné databázi pak zaplatí všechny ostatní procesy včetně sshd, takže můžete přijít o přístup k serveru. Proto doporučujeme pro databázi -600 až -900 a pro PHP-FPM kladnou hodnotu: správce poolu za zabitý worker jednoduše spustí nový.

Na stendu jsme oom_score_adj snížit nemohli: kontejneru chybí capability CAP_SYS_RESOURCE a zápis -1000 vrátil “Permission denied” (na běžném VPS jako root nebo přes systemd to funguje). Otestovali jsme proto druhou stranu a procesům PHP-FPM dali +500. Stejný požadavek na 150 MB:

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

Tentokrát dostal těžký požadavek 502, nginx zapsal recv() failed (104: Connection reset by peer) a databáze s teplou cache si ničeho nevšimla: všech 60 požadavků na úvodní stránku dostalo 200. Výpočet je jednoduchý: jádro přičetlo k 108 MB workeru polovinu limitu (160 MB) a 268 MB převážilo 212 MB databáze.

Výsledky testu: OOM killer zabije při výchozím nastavení MariaDB, při oom_score_adj +500 PHP worker a při memory_limit 128M nikoho
Stejný požadavek na 150 MB ve třech konfiguracích testovacího stendu

První obranná linie: memory_limit a pm.max_children

Nejlepší je k OOM killeru vůbec nedojít. Stejný požadavek s memory_limit = 128M v poolu PHP-FPM skončil obyčejnou fatální chybou PHP za 0,35 s:

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 2097152 bytes) in /srv/cw18/www/hog.php on line 4

Jeden požadavek se stavem 500, žádné zabité procesy a počítadlo oom_kill se nepohnulo. Nastavte proto memory_limit na to, co web opravdu potřebuje (pro WordPress obvykle 128-256 MB), ne na -1 nebo 1024M pro jistotu.

Druhou polovinou rovnice je počet workerů běžících současně. pm.max_children násobí paměť jednoho workeru a právě tady se nejčastěji chybuje, protože se sčítá RSS z top.

RSS, nebo PSS: kolik PHP-FPM worker doopravdy váží

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

Každý worker ukazuje 38 až 46 MB RSS, dohromady 164 MB. Většina těchto stránek je však sdílená: OPcache a knihovny, které si každý worker počítá jako vlastní. PSS dělí sdílené stránky rovným dílem mezi procesy a součet PSS (50 MB) je skutečná spotřeba poolu. USS (3-9 MB) se uvolní, když konkrétní worker skončí. Bez smem najdete PSS v /proc/PID/smaps_rollup.

Praktické pravidlo: pm.max_children se rovná paměti, kterou můžete dát PHP, vydělené průměrným PSS workeru ve špičce, s rezervou pro těžké požadavky. Paměť pro PHP je RAM minus buffer pool databáze, minus ostatní služby, minus 15-20 % pro souborovou cache. Velikost buffer poolu určete podle objemu dat, jak popisujeme v návodu na optimalizaci databáze. Každá verze PHP má vlastní pool s vlastním nastavením, takže když změníte verzi PHP, zkontrolujte pm.max_children v novém poolu.

Kolik swapu server potřebuje

Na webovém serveru swap není pracovní paměť, ale rezerva. Jádro tam odkládá stránky, na které dlouho nikdo nesáhl, a dává vám pár minut na to, abyste si všimli úniku paměti dřív, než přijde OOM killer. Pokud server žije ve swapu pořád, web je pomalý a pomůže jen víc RAM nebo méně procesů.

RAM serveru Red Hat (RHEL 9) Naše doporučení pro VPS s weby
do 2 GB 2 × RAM 1-2 GB
2-8 GB stejně jako RAM 2 GB
8-64 GB alespoň 4 GB 2-4 GB
nad 64 GB podle zátěže, alespoň 4 GB 4 GB

Tabulka Red Hat platí pro libovolné systémy, proto pro databázový server doporučujeme méně: velký swap jen oddálí OOM killer, zatímco server pomalu přehazuje stránky z disku. Servery nehibernují, tento sloupec proto vynecháváme.

Jak vytvořit swapovací soubor

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

Na stendu jsme spustili první tři příkazy pro soubor 512 MB (mkswap odpověděl Setting up swapspace version 1, size = 512 MiB) a swapon jsme záměrně nespustili: stroj sdílí několik testů a swap působí na celý server. Proto článek neobsahuje měření rychlosti se swapem, jen ověřenou syntaxi. Na Btrfs a některých starších systémech se fallocate pro swap nehodí; soubor pak vytvořte příkazem dd if=/dev/zero of=/swapfile bs=1M count=2048. Pokud server teprve připravujete, přidejte swap spolu s dalšími kroky z našeho kontrolního seznamu nastavení VPS s Ubuntu 24.04.

swappiness: co to číslo znamená

Podle dokumentace jádra vm.swappiness určuje relativní cenu swapu ve srovnání se zahozením souborové cache: od 0 do 200, výchozí 60. Na testovacím stroji vrátil cat /proc/sys/vm/swappiness také 60.

  • 0: jádro nezačne swapovat, dokud volné a souborové stránky neklesnou pod horní hranici (high watermark). Swap není vypnutý, stává se poslední rezervou.
  • 10: naše doporučení pro databázový server. Jádro nejdřív uvolní souborovou cache a paměť MariaDB a PHP drží v RAM.
  • 60: výchozí hodnota; swap je dražší než cache, ale používá se ochotně.
  • 100: swap a souborová cache stojí stejně.
  • nad 100: pro swap v paměti (zram, zswap); dokumentace uvádí jako příklad 133.
cat /proc/sys/vm/swappiness
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf

Příkaz sysctl platí do restartu, soubor v /etc/sysctl.d nastavení zachová trvale. Na stendu jsme je nespustili, protože jde o globální nastavení sdíleného stroje.

zram místo swapu na disku

zram vytvoří v RAM komprimované blokové zařízení, takže swap na něm funguje bez přístupu na disk. Dokumentace jádra počítá s kompresí zhruba 2:1 a zařízení zram větší než dvojnásobek RAM považuje za zbytečné. Pro VPS s 1-2 GB je to rozumná volba: v Ubuntu se zapíná balíčkem zram-tools nebo systemd-zram-generator a swappiness pro takový swap se nastavuje nad 100. zram jsme netestovali, protože jádro stendu tento modul nemá.

Pasti při počítání paměti

free -h na testovacím stroji:

               total        used        free      shared  buff/cache   available
Mem:           7.8Gi       2.4Gi       1.8Gi       112Mi       4.2Gi       5.4Gi
Swap:             0B          0B          0B

Sloupec free (1,8 GB) je paměť, kterou vůbec nic nepoužívá. Sloupec available (5,4 GB) je odhad jádra, kolik mohou nové procesy dostat bez swapování, včetně cache, kterou lze zahodit. Sledujte available: málo free při velkém buff/cache je normální chování Linuxu, ne nedostatek paměti.

Ověřili jsme to zvlášť: v cgroup s limitem 128 MB jsme dvakrát přečetli soubor 600 MB. Cache zaplnila celý limit (127 MB), jádro narazilo na limit 5 113krát a vyhodilo starší stránky, ale počítadlo oom_kill zůstalo na nule. Souborová cache OOM killer nespouští.

Třetí past jsme probrali výše: součet RSS z top nebo ps pro pool PHP-FPM nadsazuje spotřebu trojnásobně (164 MB oproti 50 MB PSS na stendu).

Příznak, příkaz, řešení

Příznak Co ukáže příkaz Co dělat
Databáze se restartuje sama, v jejím logu není důvod dmesg -T | grep -i 'killed process' ukáže mariadbd OOMScoreAdjust pro databázi, snížit pm.max_children a memory_limit
Občasné 502 na těžkých stránkách dmesg ukáže zabitý php-fpm, log PHP-FPM píše “exited on signal 9” Najít těžký skript, omezit memory_limit, přidat RAM
Chyba 500 “Allowed memory size exhausted” Chybový log PHP Optimalizovat skript nebo mírně zvýšit memory_limit
Web je pomalý, disk stále vytížený vmstat 1: sloupce si a so stále nad nulou Málo RAM: méně workerů, menší buffer pool nebo VPS s větší pamětí
free ukazuje jen 100-200 MB volné paměti available a buff/cache jsou velké Nic, je to cache
Swap se používá, ale web je rychlý vmstat 1: si a so jsou nulové Normální: leží tam nečinné stránky
Bez swapu, OOM při každé špičce swapon --show nic nevypíše Swapovací soubor 1-2 GB a swappiness 10

Pokud nechcete swap, OOM killer a pooly PHP-FPM hlídat sami, web na WordPressu nebo jiném CMS může běžet na sdíleném hostingu: u hostingových tarifů RichHost nastavuje server, PHP a databáze tým hostingu a vy řešíte jen limity svého účtu.

Časté dotazy

Můžu swap na serveru úplně vypnout?

Můžete, ale pak jádro nemá kam odložit ani nečinné stránky a každá špička skončí zabitým procesem, jako v našem testu bez swapu. Malý swap s nízkým swappiness vyjde levněji než nouzový restart databáze.

Kolik RAM potřebuje VPS pro WordPress?

Sečtěte buffer pool databáze, PSS workeru vynásobené pm.max_children a zhruba 300-500 MB pro systém a souborovou cache. Na stendu měl čistý WordPress 10-16 MB PSS na worker; weby s těžkými pluginy váží víc, takže si ten svůj změřte. Pokud paměť chybí trvale, porovnejte ceny VPS serverů u tarifů s větší RAM.

Proč OOM killer nezabil proces, který paměť snědl?

Viníka nehledá, vybírá proces s nejvyšším skóre: rezidentní paměť, swap a tabulky stránek plus oom_score_adj. Velká databáze je téměř vždy těžší než jeden PHP worker.

Existuje OOM killer i na sdíleném hostingu?

Tam paměť serveru spravuje poskytovatel a web naráží na limity svého účtu: PHP memory_limit, počet procesů, chyba 508. Swap, swappiness ani oom_score_adj na sdíleném hostingu nastavovat nemusíte a ani nemůžete.

Více k tématu

Všechny články
HTTP/3 a QUIC na hostingu: skutečný přínos a jak je zapnout
Výkon hostingu28.07.2026

HTTP/3 a QUIC na hostingu: skutečný přínos a jak je zapnout

Otestovali jsme HTTP/1.1, HTTP/2 a HTTP/3 na nginx 1.28: co QUIC webu skutečně přinese, kdy nepomůže, jak zapnout HTTP/3, otevřít port UDP…

Cron úlohy na hostingu: nastavení a nejčastější chyby
Konfigurace serverů12.06.2026

Cron úlohy na hostingu: nastavení a nejčastější chyby

Jak přidat cron úlohu v panelu nebo přes crontab, proč příkaz funguje přes SSH, ale v cronu selže, a čím se WP-Cron…

IOPS a latence disku: co je pro web důležité a jak je změřit
Konfigurace serverů03.06.2026

IOPS a latence disku: co je pro web důležité a jak je změřit

Co znamenají IOPS, latence a propustnost, proč WordPress a MySQL závisí na čtení 4K a na fsync, a ne na MB/s, a…