Configurare lo swap ed evitare i crash per memoria esaurita
Servizio sparito, nessun rapporto di crash, il logfile si interrompe a metà frase: come dimostrare un OOM-Kill, creare un file di swap pulito e riconoscere quando lo swap si limita a rimandare il problema.
Il servizio è sparito. Nessun rapporto di crash, nessuno stacktrace, il logfile si interrompe a metà riga. MariaDB non risponde più, nginx restituisce 502, il server Minecraft è offline e nell'applicazione stessa non c'è niente di sospetto. Lo schema è quasi sempre lo stesso: il kernel non aveva più memoria libera e ha terminato un processo per tenere in vita il sistema. Questo articolo ti mostra come dimostrarlo senza ombra di dubbio, come creare un file di swap in modo pulito e renderlo permanente, e in quali casi lo swap si limita a rimandare il problema di qualche minuto.
Portare la prova: dmesg e journalctl
Prima di configurare qualsiasi cosa ti serve la prova. A ogni OOM-Kill (Out of Memory) il kernel scrive un blocco dettagliato nel ring buffer:
dmesg -T | grep -iE 'out of memory|oom-kill'
Se non torna nessuna riga e il comando termina con exit code 1, dall'ultimo avvio non c'è stato nessun OOM del kernel. L'exit code non è un errore, è solo la normale risposta di grep quando non trova nulla. Se invece qualcosa torna, su tutti e quattro i sistemi trattati qui si presenta così:
mariadbd invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0
oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/system.slice/mariadb.service,task=mariadbd,pid=1043,uid=107
Out of memory: Killed process 1043 (mariadbd) total-vm:2894760kB, anon-rss:1583204kB, file-rss:0kB, shmem-rss:0kB, UID:107 pgtables:3820kB oom_score_adj:0
Tre dettagli sono importanti. Primo: invoked oom-killer indica il processo che ha richiesto la memoria, non necessariamente il colpevole. Secondo: il processo ucciso compare nella riga Killed process. Terzo: quello che conta è anon-rss, cioè la memoria anonima realmente occupata. total-vm è spazio di indirizzamento riservato e con Java o Go è regolarmente un multiplo di quel valore, quindi non dice nulla.
Se esegui dmesg senza root, su Debian 12, Debian 13, Ubuntu 22.04 e Ubuntu 24.04 risponde con dmesg: read kernel buffer failed: Operation not permitted, perché kernel.dmesg_restrict è impostato ovunque a 1. Lavora quindi con sudo oppure come root. Se il messaggio resta anche da root, sei dentro un container LXC o OpenVZ che non ha accesso al ring buffer del sistema host. In quel caso l'unica strada è la seconda, via journalctl -k.
Il ring buffer si svuota a ogni riavvio. Per questo esiste la seconda strada, quella del journal, e qui c'è la trappola che quasi tutte le guide ignorano:
journalctl -k --grep "Out of memory"
-k implica -b e mostra quindi soltanto il boot corrente. Se la macchina è stata riavviata dopo l'incidente, questo comando non restituisce nulla anche se l'evento è stato registrato. Per il boot precedente oppure per un intervallo di tempo:
journalctl -k -b -1 --grep "Out of memory"
journalctl --since "7 days ago" --grep "Out of memory|oom-kill"
Se il primo dei due comandi risponde con No journal boot entry found for the specified boot (-1), nel journal semplicemente non è salvato nessun boot precedente. Neanche questo è un errore: è il caso normale su un sistema il cui journal registra solo dall'avvio corrente.
Tutto questo funziona solo se il journal è davvero persistente. Verificalo:
ls -d /var/log/journal
Su Debian e Ubuntu questa directory esiste di serie, quindi il test dà quasi sempre un risultato e il mkdir successivo gira a vuoto senza rompere nulla. Se per una volta la directory non ci fosse, il journal starebbe solo in /run e andrebbe perso a ogni riavvio. Si rimedia con due comandi:
mkdir -p /var/log/journal
systemctl restart systemd-journald
OOM del kernel o limite systemd? Due cause diverse
Questa distinzione decide se lo swap serva davvero a qualcosa. Guarda il campo constraint nel messaggio del kernel.
CONSTRAINT_NONE significa: la memoria è finita all'intero sistema. Qui lo swap aiuta.
CONSTRAINT_MEMCG significa: solo una singola control group ha superato il proprio limite, mentre il resto del sistema aveva aria in abbondanza. Qui lo swap non serve, qui deve salire il limite oppure l'applicazione deve consumare meno. Questi kill li riconosci anche dal servizio stesso:
systemctl status mariadb
Se lì compare Main process exited, code=killed, status=9/KILL e più sotto Failed with result 'oom-kill', si è trattato di un OOM-Kill. Se poi ci sia di mezzo un limite cgroup lo rivela un file contatore. Richiede cgroup v2, che è lo standard su tutte e quattro le distribuzioni; con il più vecchio cgroup v1 il percorso non esiste:
cat /sys/fs/cgroup/system.slice/mariadb.service/memory.events
systemctl show mariadb -p MemoryMax -p MemoryHigh
Un valore maggiore di zero in oom_kill insieme a un MemoryMax impostato è la prova di un limite locale.
C'è ancora un terzo candidato, spesso trascurato perché non scrive assolutamente nulla in dmesg: systemd-oomd. Il servizio lavora in userspace, valuta l'indicatore di pressione PSI e termina intere control group prima ancora che il kernel intervenga. Il suo messaggio nel journal suona più o meno così: Killed /system.slice/... due to memory pressure for /system.slice being 60.00% > 50.00% for > 20s with reclaim activity. Verifica se è in esecuzione:
systemctl is-active systemd-oomd
Su Ubuntu systemd-oomd è installato e attivo di serie dalla 22.04, su Debian non fa parte della dotazione standard. Un inactive su un server Debian è quindi previsto e non è il segnale di un problema.
Importante per dopo: systemd-oomd può scattare anche perché si riempie lo swap. Con oomd attivo, più swap può quindi anticipare le interruzioni invece di rimandarle.
Prima dello swap: quanta memoria manca davvero
Due minuti di misure ti risparmiano una decisione sbagliata.
free -h
Interessa esclusivamente la colonna available, non free. La cache conta come disponibile e viene liberata quando serve. Chi legge la colonna free considera sovraccarico qualsiasi sistema sano.
ps -eo pid,comm,rss,%mem --sort=-rss | head -n 11
systemd-cgtop --order=memory -b -n 1
La prima riga mostra i singoli processi più grandi, la seconda raggruppa per servizio. Nella pratica lì compaiono quasi sempre gli stessi tre sospetti: MariaDB con un innodb_buffer_pool_size scelto troppo grande, PHP-FPM con un pm.max_children troppo alto e una JVM con un -Xmx troppo generoso.
Come valore di partenza per il file di swap questa tabella basta. Su un server più grande non significa migliore, perché uno swap che arrivi davvero a usare del tutto rende la macchina inutilizzabile.
| RAM | File di swap sensato |
|---|---|
| 1 GB | da 1 a 2 GB |
| 2 GB | 2 GB |
| da 4 a 8 GB | da 2 a 4 GB |
| 16 GB e oltre | 4 GB, raramente di più |
Creare il file di swap
Per prima cosa controlla se uno swap esiste già. I server Ubuntu installati dall'ISO portano spesso già /swap.img, Debian dall'installer di solito una vera partizione di swap. Le immagini cloud di entrambe le distribuzioni in genere non hanno nulla.
swapon --show
Se l'output resta vuoto, non c'è swap. Come passo successivo verifica il filesystem, perché da questo dipende la procedura:
findmnt -no FSTYPE -T /
Con ext4 o xfs si prosegue direttamente. Crea il file con dd, non con fallocate. fallocate è più veloce, ma a seconda del filesystem e del kernel produce un file con aree non scritte, e allora swapon lo rifiuta. dd scrive zeri veri e funziona ovunque:
dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
chmod 600 /swapfile
Prima del passo successivo vale la pena fare un controllo che in molti saltano. Se sotto /swapfile c'è già uno swap attivo, magari da un tentativo precedente o dall'impostazione predefinita dell'immagine, mkswap rifiuta di lavorare con mkswap: error: /swapfile is mounted; will not make swapspace. Quello che conta è soltanto se il percorso risulta attivo in /proc/swaps. Quindi controlla e, se serve, disattiva:
swapon --show
swapoff /swapfile
Se in swapon --show non compare nessuna riga con /swapfile, puoi saltare lo swapoff. Dopodiché scrivi l'area di swap:
mkswap /swapfile
mkswap risponde con Setting up swapspace version 1, size = 2 GiB (2147479552 bytes) e un nuovo UUID. Solo a quel punto attivalo:
swapon /swapfile
L'ordine non è negoziabile. chmod prima di mkswap, mkswap prima di swapon, ed eventualmente swapoff prima di tutto il resto.
Come capisci che ha funzionato davvero
Il fatto che swapon giri senza messaggi di errore non è ancora una prova. La prova sono questi due output:
swapon --show
free -h
swapon --show deve produrre una riga con /swapfile, file, la dimensione e una priorità. In free -h la riga Swap: deve essere passata da 0B alla nuova dimensione. Se uno dei due output resta invariato, lo swap non è attivo, indipendentemente da quello che il comando ha segnalato prima.
Rendere permanente senza rischiare il boot
Uno swapon non sopravvive a un riavvio. La voce va inserita in /etc/fstab, ed è esattamente lì che i server vengono fatti a pezzi. Prima il backup:
cp /etc/fstab /etc/fstab.bak
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Fai attenzione ai due segni di maggiore. Un solo > sovrascrive l'intero file, e a quel punto la macchina non si avvia più correttamente. Poi verifica la sintassi, prima di riavviare:
findmnt --verify
Un avviso qui è del tutto normale e non è un motivo per rimuovere di nuovo la riga: [W] non-bind mount source /swapfile is a directory or regular file. Con un file di swap è inevitabile, e il comando termina comunque con valore di ritorno 0. Se al file manca anche un header di swap valido, si aggiunge [W] cannot detect on-disk filesystem type, e allora hai davvero dimenticato un mkswap.
Se sopra hai già attivato lo swap a mano con swapon /swapfile, adesso è già attivo. Altrimenti swapon -a attiva tutte le voci della fstab senza che tu debba riavviare. Il vero test però è un altro. systemd genera da ogni riga della fstab una unit dedicata, per /swapfile si chiama swapfile.swap. Se questa unit compare ed è attiva, al prossimo avvio lo swap verrà montato con certezza:
systemctl daemon-reload
systemctl list-units --type swap
Ti aspetti una riga swapfile.swap loaded active active Swap. Se manca, la riga della fstab non è corretta e un riavvio finirebbe senza swap. Solo quando questo torna vale la pena riavviare, con il controllo successivo tramite swapon --show.
Impostare swappiness nel modo giusto
Il parametro del kernel vm.swappiness regola quanto volentieri le pagine anonime finiscono in swap invece di scartare la cache dei file. Il valore predefinito su Debian 12, Debian 13, Ubuntu 22.04 e Ubuntu 24.04 è identico, 60:
cat /proc/sys/vm/swappiness
Dal kernel 5.8 l'intervallo dei valori va da 0 a 200, e tutte e quattro le distribuzioni hanno un kernel più recente. Due equivoci diffusi: vm.swappiness=0 non disattiva lo swap, impedisce soltanto lo spostamento preventivo e lascia comunque che il kernel usi lo swap prima di uccidere. E un valore basso non rende più veloce un sistema a cui la memoria semplicemente manca.
Valori sensati: da 10 a 20 sui server di database, 60 sui web server misti, 100 e oltre se usi zram. Per renderlo permanente va messo in un file dedicato sotto /etc/sysctl.d/, non nel /etc/sysctl.conf, che dà fastidio durante gli aggiornamenti dei pacchetti:
echo 'vm.swappiness = 10' > /etc/sysctl.d/99-swappiness.conf
sysctl --system
cat /proc/sys/vm/swappiness
Il terzo comando è il controllo di riuscita. sysctl --system legge tutte le directory in un ordine fisso, e un file già presente con un numero più alto può sovrascrivere il tuo valore.
Quando qualcosa va storto: i messaggi di errore parola per parola
swapon: /swapfile: insecure permissions 0644, 0600 suggested. Solo un avviso, lo swap funziona comunque. Sistemalo lo stesso, altrimenti qualsiasi utente può leggere il contenuto dei processi finiti in swap: chmod 600 /swapfile.
swapon: /swapfile: swapon failed: Invalid argument L'errore più frequente. O è stato dimenticato mkswap, oppure il file contiene buchi. Nel log del kernel compare allora anche swapon: swapfile has holes. Soluzione: cancella il file e ricrealo con dd invece che con fallocate.
Su btrfs vale lo stesso errore, più BTRFS warning: swapfile must not be copy-on-write. Qui l'ordine è diverso: il file va creato vuoto e marcato come non copy-on-write prima di riempirlo. Un avvertimento in anticipo, che può decidere le sorti di un server in produzione: truncate -s 0 e rm vanno a buon fine anche quando sotto quel percorso è ancora montato uno swap attivo. Il kernel punta poi a blocchi che non esistono più. Se swapon --show elenca il percorso come attivo, prima ci vuole quindi obbligatoriamente uno swapoff /swapfile.
truncate -s 0 /swapfile
chattr +C /swapfile
Poi riempi come al solito con dd, chmod 600, mkswap, swapon. Su subvolumi compressi e negli snapshot lo swap continua a non funzionare.
swapon: /swapfile: swapon failed: Operation not permitted Sei dentro un container. LXC, OpenVZ e Docker condividono il kernel dell'host e non possono attivare uno swap proprio. Verifica con:
systemd-detect-virt
Se il comando risponde kvm, qemu oppure none, gira un kernel proprio e lo swap è possibile. Se risponde lxc, openvz o docker, l'unica strada è più RAM oppure un prodotto con virtualizzazione completa. I server root KVM e i server dedicati di KernelHost hanno un kernel proprio, lì lo swap si configura senza limitazioni.
dd: error writing '/swapfile': No space left on device Il disco è troppo pieno. Prima df -h, poi rimuovi con rm /swapfile il file scritto a metà, altrimenti continua a occupare spazio. Vale anche qui: se swapon --show mostra il percorso come attivo, prima della cancellazione ci vuole uno swapoff /swapfile.
Il sistema non si avvia più, compare la console di emergenza. Quasi sempre un errore di battitura in /etc/fstab. Accedi tramite la console nell'area clienti, poi mount -o remount,rw /, rimuovi la riga sbagliata oppure ricopia /etc/fstab.bak, riavvia. È esattamente per questo che prima hai creato la copia di backup.
Differenze tra Debian 13, Debian 12, Ubuntu 24.04 e 22.04
I comandi sono identici su tutti e quattro i sistemi, lo stato di partenza no.
- Swap già presente: Ubuntu Server installato dall'ISO crea spesso
/swap.img, Debian dall'installer una partizione di swap. Le immagini cloud di entrambe le distribuzioni arrivano senza swap. Sempre primaswapon --show. - systemd-oomd: su Ubuntu installato e attivo di serie dalla 22.04, su entrambe le versioni Debian non fa parte della dotazione standard. Questo spiega perché software identico può morire in modo diverso su due sistemi apparentemente uguali.
- Database: Debian 12 e Debian 13 non forniscono
mysql-server, lì gira sempre MariaDB (10.11 su Debian 12, 11.8 su Debian 13). Ubuntu 22.04 e 24.04 hanno entrambi. I valori predefiniti diinnodb_buffer_pool_sizedifferiscono di conseguenza, e proprio questo valore è sui server piccoli la causa di OOM più frequente. - Java: Debian 12 conosce solo OpenJDK 17, Debian 13 solo OpenJDK 21, Ubuntu 22.04 e 24.04 coprono dalla 8 alla 21. Una JVM senza
-Xmximpostato si prende di default un quarto della RAM, e con più istanze l'OOM-Kill è già annunciato. - Messaggi del kernel: formato e testo del messaggio OOM sono uguali su tutti e quattro i sistemi, gli esempi qui sopra valgono ovunque.
- swappiness: ovunque 60 come valore predefinito.
Quando lo swap aiuta e quando si limita a rimandare il problema
Lo swap aiuta in modo affidabile con i picchi brevi, per esempio durante un backup, un aggiornamento dei pacchetti o un import notturno. Aiuta con i servizi che occupano molta memoria e poi restano inattivi per giorni, perché quelle pagine possono tranquillamente stare sul disco. E nel caso peggiore ti regala minuti in cui la sessione SSH risponde ancora e puoi intervenire, invece di trovarti davanti a un server morto.
Lo swap non aiuta quando il fabbisogno costante supera semplicemente la RAM disponibile. A quel punto il sistema comincia a fare thrashing: sposta pagine avanti e indietro senza sosta, il load sale a valori a due cifre, l'uso della CPU resta basso, tutto aspetta l'I/O. In pratica è peggio di un OOM-Kill pulito, perché non passa più nemmeno il login. Due comandi ti dicono se sei in questo stato:
vmstat 1 5
test -e /proc/pressure/memory && cat /proc/pressure/memory
Con vmstat contano le colonne si e so. Valori stabilmente a tre cifre significano scambio attivo in entrambe le direzioni. In /proc/pressure/memory è decisivo full avg10: valori sopra 10 vogliono dire che l'intero sistema passa il dieci per cento del tempo ad aspettare memoria. Sopra 40 la macchina è praticamente morta. Il file però esiste solo se nel kernel è attivo Pressure Stall Information. I kernel standard di Debian e Ubuntu lo includono, altri kernel non necessariamente. Da qui la protezione con test -e: se il file manca, l'output resta vuoto invece di farti scambiare un No such file or directory per un guasto. PSI si può abilitare a posteriori con il parametro di boot del kernel psi=1.
Quali processi si trovino davvero in swap te lo dice questa riga:
grep VmSwap /proc/*/status | sort -k2 -rn | head
Lo swap non serve neppure contro un limite cgroup (CONSTRAINT_MEMCG), contro la memoria bloccata con mlock e contro una JVM il cui heap è configurato più grande della RAM disponibile. Il garbage collector percorre regolarmente l'intero heap e riporta subito indietro ogni pagina finita in swap.
I tre strumenti per quando lo swap non basta
zram crea un'area di swap compressa nella RAM stessa. È più veloce di ordini di grandezza rispetto a un file sul disco e, a seconda dei dati, ti regala dal 20 al 40 per cento di memoria effettiva in più:
apt update
apt install -y zram-tools
La configurazione sta in /etc/default/zramswap tramite ALGO=zstd e PERCENT=50, poi systemctl restart zramswap. Controllo con zramctl e di nuovo swapon --show, dove compare allora /dev/zram0. Con zram vm.swappiness va alzato tra 100 e 180, perché qui spostare in swap costa poco.
earlyoom interviene prima che il kernel congeli il sistema e uccide in modo mirato invece che per euristica:
apt install -y earlyoom
In /etc/default/earlyoom imposti tramite EARLYOOM_ARGS le soglie e proteggi i processi importanti, per esempio con -m 5 -s 5 --avoid '(^|/)(sshd|systemd)$'. Così il login SSH resta raggiungibile mentre cade il divoratore di memoria.
Limiti fissi per servizio sono la soluzione più pulita quando un determinato servizio sfora regolarmente. Con systemctl edit mariadb inserisci MemoryHigh=1200M e MemoryMax=1500M. Il servizio viene allora frenato e, se necessario, terminato da solo, invece di trascinarsi dietro l'intero server. Controllo con systemctl show mariadb -p MemoryMax.
Nella maggior parte dei casi però il fix vero resta la configurazione dell'applicazione: innodb_buffer_pool_size su una misura realistica, pm.max_children in base al fabbisogno di memoria reale per worker, un -Xmx impostato per ogni JVM. Lo swap è la rete di sicurezza, non la soluzione.
Come tornare indietro, se non va bene
La strada di ritorno è breve e va percorsa in quest'ordine:
swapoff /swapfile
Con uno swap molto occupato il comando può girare per diversi minuti, perché tutte le pagine devono tornare in RAM. Se finisce su swapoff: /swapfile: swapoff failed: Cannot allocate memory, in RAM non c'è posto per il trasferimento di ritorno e allora vanno prima fermati dei servizi. Solo dopo uno swapoff riuscito rimuovi la riga della fstab e cancella il file:
rm /swapfile
swapon --show
Cancellare il file finché è ancora montato porta a un sistema la cui gestione della memoria punta a un inode che non esiste più. Finisce male. Alla fine ancora un free -h e un riavvio come controprova.
Domande frequenti
Quanto swap serve su un server?
Perché journalctl -k non mostra nessun OOM-Kill anche se c'è stato?
Come capisco se il processo è stato terminato dal kernel o da un limite systemd?
Perché swapon fallisce con "Invalid argument"?
Posso configurare lo swap in un container?
vm.swappiness=0 significa che non viene più spostato nulla in swap?
2026 KernelHost GmbH. Tutti i diritti riservati. Questa guida è protetta dal diritto d'autore. La ripubblicazione su altri siti web, anche parziale o in forma modificata, non è consentita senza il nostro consenso scritto. Le citazioni con indicazione della fonte e un link sono le benvenute.

