Perché dopo l'upgrade a Debian 13 il backup notturno fallisce con /tmp in RAM
Da Debian 13 trixie la directory /tmp non vive più sul disco: è montata come tmpfs, un filesystem che sta in RAM, con un tetto di default pari al 50% della memoria fisica. Su un VPS da 4 GB significa che /tmp può contenere al massimo 2 GB, qualunque sia lo spazio libero sul disco. La parte insidiosa è che per i sistemi aggiornati da bookworm il nuovo comportamento non parte durante l'upgrade: si attiva al primo riavvio successivo, tipicamente quello che fai per caricare il kernel nuovo. Il risultato è un guasto con la miccia lunga: l'upgrade sembra perfetto, i servizi ripartono, i controlli passano, e qualche notte dopo il backup muore con No space left on device mentre df giura che il disco è mezzo vuoto. Qualche settimana fa, durante un giro di migrazioni infrastrutturali su un parco di VPS per una PMI, ho visto esattamente questa sequenza: dump notturno fallito, disco al 40%, e un sysadmin convinto di avere un filesystem corrotto. Non c'era niente di corrotto: c'era un default nuovo che nessuno aveva letto. In questo articolo ricostruisco la catena del guasto passo per passo, spiego perché se ne accorge il backup e non l'upgrade, e mostro le tre correzioni corrette, più una che vedo consigliare spesso e che peggiora le cose.
TL;DR
- Causa: da Debian 13
/tmpè tmpfs in RAM (tetto: 50% della memoria); sui sistemi aggiornati si attiva al primo riavvio post-upgrade. Un job che scrive più spazio di quel tetto fallisce conENOSPCa disco vuoto.- Diagnosi:
findmnt /tmpmostratmpfs;df -h /tmpmostra la taglia dimezzata rispetto alla RAM.- Fix rapido: sposta lo staging del backup su disco (
TMPDIR=/var/tmpo una directory dedicata).- Fix strutturale:
systemctl mask tmp.mounte riavvio per tornare a/tmpsu disco, oppure ridimensiona il tmpfs con un override ditmp.mount.- Da non fare: aggiungere swap per "dare spazio" a
/tmp: sposta il problema sulla memoria e rallenta tutto.
Perché il backup fallisce se il disco è mezzo vuoto?
La risposta secca: perché il backup non sta scrivendo sul disco. Le release notes ufficiali di trixie lo dichiarano senza giri di parole: da questa release il default è che /tmp venga tenuto in memoria tramite tmpfs, con un'allocazione massima del 50% della RAM, e per i sistemi aggiornati da bookworm "il nuovo comportamento parte solo dopo un riavvio". Quel 50% è un tetto, non una prenotazione: la memoria viene consumata solo quando i file vengono effettivamente creati. Ed è proprio questa proprietà a rendere il problema invisibile fino alla prima scrittura pesante.
La verifica richiede dieci secondi e conviene farla adesso, non alla prossima emergenza:
findmnt /tmp
# TARGET SOURCE FSTYPE OPTIONS
# /tmp tmpfs tmpfs rw,nosuid,nodev,size=...
df -h /tmp
# Filesystem Size Used Avail Use% Mounted on
# tmpfs 2.0G 12K 2.0G 1% /tmpSe findmnt risponde tmpfs e la colonna Size di df vale circa metà della tua RAM, sei nel nuovo regime. Su un VPS da 4 GB quella Size è 2 GB: il dump compresso da 2,8 GB che il tuo script appoggia in /tmp prima di spedirlo altrove non ci entrerà mai, e tar o gzip moriranno a metà con un errore che, letto senza contesto, sembra un problema di disco.
Il punto che disorienta è la geometria temporale del guasto. L'upgrade a trixie in sé non tocca /tmp: finché non riavvii, la directory resta quella classica sul filesystem di root. Il riavvio del kernel, che fai magari a tarda sera a upgrade concluso, arma la trappola. La prima notte il backup può perfino riuscire, se il dump è piccolo o se la retention locale è appena stata ripulita. Poi il database cresce, o si accumula un secondo file di staging, e il job esplode. A quel punto tra la causa (l'upgrade) e il sintomo (il backup fallito) possono esserci giorni di distanza, ed è esattamente il tipo di correlazione che un occhio non allenato non fa.
Nel mio lavoro di gestione di VPS unmanaged per PMI, la differenza fra un'infrastruttura presidiata e una lasciata a se stessa la fanno proprio questi cambi di default letti prima che mordano: nel mio profilo professionale trovi come imposto la manutenzione dei server Debian e Ubuntu in produzione, upgrade compresi, con i controlli post-riavvio che intercettano queste trappole prima della prima notte.
La catena del guasto: perché se ne accorge il backup e non l'upgrade
Vale la pena ricostruire la sequenza completa, perché contiene una lezione più generale sulla diagnosi. Primo anello: l'upgrade da bookworm a trixie si conclude, i pacchetti sono aggiornati, /tmp è ancora una directory su disco. Secondo anello: riavvii per il kernel nuovo, systemd attiva l'unità tmp.mount e da quel momento /tmp è un tmpfs vuoto. Terzo anello: tutti i processi "normali" continuano a funzionare, perché scrivono in /tmp file da kilobyte: socket, lock, file temporanei di editor e script. I tuoi controlli post-upgrade passano. Quarto anello: il primo processo che scrive gigabyte in /tmp è quasi sempre il backup notturno, perché è l'unico job del sistema che per costruzione crea file grandi quanto i tuoi dati. Ed è lui a trovare il tetto.
C'è però un secondo modo, più subdolo, in cui questa configurazione può fare danni: il tmpfs non consuma disco, consuma memoria. Ogni gigabyte scritto in /tmp è un gigabyte sottratto a page cache, buffer e applicazioni. Su un VPS da 4 GB con MariaDB, PHP-FPM e il job di backup in corsa, riempire 1,5 GB di tmpfs significa mettere il sistema sotto pressione di memoria: il kernel inizia a spingere pagine in swap, le query rallentano, e nel caso peggiore interviene l'OOM killer, che con una certa affezione statistica sceglie proprio il processo più grosso, cioè il database. Ho visto letture di questo scenario in cui la diagnosi iniziale era "MariaDB è instabile dopo l'upgrade": no, era il backup che soffocava la macchina scrivendo il dump in RAM.
Il backup che fallisce con
ENOSPCè il caso fortunato: fa rumore, lascia un errore nei log, ti costringe a guardare. Il caso cattivo è il tmpfs che manda in pressione la memoria e ti uccide il database alle tre di notte, con un sintomo che punta ovunque tranne che alla causa.
La conferma di questo secondo scenario si cerca in due posti: dmesg -T | grep -i oom per gli interventi dell'OOM killer, e journalctl -b -u mariadb per i riavvii del servizio in orario notturno. Se trovi entrambi in corrispondenza della finestra del backup, la catena è quella. E a proposito di finestra: se non ricordi con precisione quando girano davvero i tuoi job, incolla le righe del crontab nel mio interprete di espressioni cron, che le traduce in linguaggio naturale e mostra le prossime esecuzioni; l'ambiguità sugli orari è una delle ragioni per cui queste correlazioni sfuggono.
Le tre correzioni giuste, e quella sbagliata
La prima correzione, la più chirurgica, è spostare lo staging del backup fuori da /tmp. È anche la più sana concettualmente: un file da gigabyte che deve sopravvivere qualche minuto non ha niente da guadagnare dalla RAM. Quasi tutti gli script e i tool rispettano la variabile TMPDIR, e in ogni caso la directory di lavoro di un backup dovrebbe essere esplicita:
# Nello script di backup: staging su disco, non in /tmp
STAGING=/var/backups/staging
mkdir -p "$STAGING"
export TMPDIR="$STAGING"
mysqldump --single-transaction --routines nomedb | gzip > "$STAGING/db-$(date +%F).sql.gz"Nota che /var/tmp resta su disco anche in trixie ed è il candidato naturale per i temporanei grandi; tienilo però d'occhio in ottica pulizia automatica, come vedremo tra un attimo. La seconda correzione è tornare al comportamento classico, che è la strada giusta se la macchina è piccola, il carico è fatto di job che appoggiano file grandi e non hai voglia di inseguire ogni script: le release notes indicano il metodo canonico,
systemctl mask tmp.mount
# al riavvio successivo /tmp torna a essere una directory sul discoed è una scelta legittima, non un ripiego: su un VPS con 2-4 GB di RAM il beneficio prestazionale di un /tmp in memoria è marginale rispetto al rischio operativo. La terza correzione è la via di mezzo: tenere il tmpfs ma dimensionarlo con criterio, riducendo il tetto (o alzandolo, su macchine con molta RAM e temporanei piccoli) tramite un override dell'unità di mount:
systemctl edit tmp.mount[Mount]
Options=mode=1777,strictatime,nosuid,nodev,size=512MCon size=512M il tmpfs smette di poter mangiare metà della memoria, e un job che sbaglia bersaglio fallisce subito, in modo evidente, invece di mettere in pressione l'intero sistema. Per una modifica al volo senza riavvio vale anche mount -o remount,size=512M /tmp, utile per testare la taglia prima di scriverla nell'override.
La correzione sbagliata, che continuo a leggere nei forum, è aggiungere swap per "fare spazio" a /tmp. Tecnicamente le pagine di un tmpfs possono finire in swap, quindi il trucco sembra funzionare: il dump da 3 GB "entra". In pratica hai trasformato una scrittura sequenziale su disco, che è il caso migliore possibile per qualunque storage, in un giro assurdo: i dati passano dalla RAM, mettono in pressione la memoria di tutti i processi, e vengono ricacciati su disco dallo swapper, con pattern di I/O peggiori e latenze imprevedibili per il database che nel frattempo sta servendo query. La swap su un VPS ha un ruolo preciso, che ho descritto nella guida alla configurazione del file di swap su Debian e Ubuntu: assorbire i picchi e dare margine all'OOM, non fare da disco travestito per i temporanei.
C'è anche la pulizia automatica: 10 giorni per /tmp, 30 per /var/tmp
Il passaggio a tmpfs è la metà rumorosa del cambiamento; l'altra metà è silenziosa e riguarda la cancellazione automatica dei file vecchi. Sulle installazioni nuove di trixie, systemd-tmpfiles cancella i file di /tmp non usati da più di 10 giorni (oltre che a ogni riavvio, implicito nel tmpfs) e quelli di /var/tmp dopo 30 giorni. Sui sistemi aggiornati da bookworm, invece, questa pulizia è stata resa opt-in: l'upgrade crea il file /etc/tmpfiles.d/tmp.conf che ripristina il comportamento storico. La documentazione di systemd-tmpfiles descrive il meccanismo nel dettaglio, e la verifica sul campo è immediata:
cat /etc/tmpfiles.d/tmp.conf 2>/dev/null
systemd-tmpfiles --cat-config | grep -E '^[qQDd] /(tmp|var/tmp)'Perché ti riguarda: se gestisci un parco misto, con macchine installate da zero su trixie e macchine aggiornate, lo stesso script si comporta in modo diverso a seconda della storia della macchina. Un'applicazione che appoggia in /var/tmp file di stato longevi, code di spool artigianali, export che qualcuno scarica "quando si ricorda", su una macchina installata da zero se li vedrà sparire dopo 30 giorni, e su una aggiornata no. La regola igienica che applico è indipendente dal default del momento: in /tmp e /var/tmp vivono solo file che il processo può rigenerare o perdere senza conseguenze, creati con mktemp; tutto ciò che ha un ciclo di vita più lungo di un'esecuzione ha una directory sua sotto /var/lib o /var/backups, con una retention decisa da te e non dal default della distribuzione.
La diagnosi replicabile, dall'inizio alla fine
Chiudo con la sequenza completa che puoi replicare su ogni macchina appena portata a trixie, con l'esito atteso di ogni passo. Uno: findmnt /tmp e df -h /tmp, per sapere in che regime sei e con quale tetto. Due: grep -r tmp /etc/fstab /etc/systemd/system/tmp.mount.d/ 2>/dev/null per scoprire se qualcuno prima di te ha già forzato una configurazione. Tre: un test di scrittura onesto, della taglia del tuo dump reale, per esempio fallocate -l 3G /tmp/probe && rm /tmp/probe: se fallisce qui, fallirà stanotte. Quattro: la lettura dei log della notte incriminata, journalctl --since "03:00" --until "04:00" più dmesg -T | grep -i oom, per distinguere il fallimento pulito da quello che ha coinvolto l'OOM killer. Cinque: la correzione scelta fra le tre di sopra, seguita da un'esecuzione manuale del backup completo, perché un backup si considera riparato quando è girato per intero, non quando il test di scrittura passa. Il quadro generale della gestione dello spazio, inode e file cancellati ma ancora aperti compresi, l'ho trattato nella guida ai problemi di spazio disco su VPS, che è il complemento naturale di questa diagnosi.
Se invece l'upgrade a trixie devi ancora farlo, parti dalla procedura passo per passo che ho documentato e aggiungi il controllo di /tmp alla lista dei check post-riavvio: è un minuto speso bene. E se il tuo backup notturno è fallito stanotte con un errore che non torna, o vuoi che qualcuno faccia il giro completo di verifica su un parco macchine appena aggiornato, staging dei backup, dimensionamento del tmpfs, retention e prova di ripristino inclusi, contattami per una consulenza: la differenza fra un backup che sembra girare e un backup che ripristina davvero si scopre sempre nel momento peggiore, a meno di non andarla a cercare prima.