---
title: "Perché il tuo mysqldump notturno probabilmente non si ripristina"
description: "Dump troncati, DEFINER che bloccano l'import, routine mai incluse: i cinque modi in cui un mysqldump fallisce in silenzio e il collaudo di ripristino automatico che li scopre."
canonical: "https://www.mauriziofonte.it/blog/post/backup-verificati-mysqldump-ripristino-test-automatico.html"
author: "Maurizio Fonte"
date: "2026-11-05T09:13:46+01:00"
modified: "2026-11-05T09:13:46+01:00"
section: "Business Continuity"
tags: ["Business Continuity", "Database"]
license: "Riuso e citazione consentiti con attribuzione e link alla fonte"
---

[Business Continuity](https://www.mauriziofonte.it/blog/cat/business_continuity.html) [Database](https://www.mauriziofonte.it/blog/cat/database.html)

# [Perché il tuo mysqldump notturno probabilmente non si ripristina](https://www.mauriziofonte.it/blog/post/backup-verificati-mysqldump-ripristino-test-automatico.html)

Giovedì 5 Novembre 2026, alle 09:13 ![Perché il tuo mysqldump notturno probabilmente non si ripristina](https://www.mauriziofonte.it/image/005800200-backup-verificati-mysqldump-ripristino-test-automatico.jpg)

C'è una domanda che faccio in ogni subentro su un'infrastruttura con database, e la risposta è quasi sempre la stessa. La domanda: "quando avete ripristinato un backup l'ultima volta?". La risposta: un silenzio, seguito da "beh, il backup gira ogni notte". Sono due affermazioni che non si toccano nemmeno. Il job notturno che produce un file è una **speranza**; il backup è un'altra cosa, è un file di cui hai dimostrato che si ripristina, e fra le due c'è un catalogo di fallimenti silenziosi che ho visto materializzarsi tutti, almeno una volta, su macchine reali: dump troncati da mesi con l'exit code perso in una pipe, snapshot internamente incoerenti presi su un database in piena attività, stored procedure e trigger che nel dump non sono mai entrati perché nessuno sapeva che vanno chiesti esplicitamente, file perfetti che si rifiutano di importare sul server di emergenza per un DEFINER che punta a un utente inesistente. In questo articolo faccio due cose, nell'ordine giusto: prima il catalogo dei cinque modi in cui un `mysqldump` tradisce senza produrre nessun errore visibile, perché la paranoia informata è la base del metodo; poi il tutorial completo del **collaudo di ripristino automatico**, il job settimanale che ripristina davvero il dump in un container usa e getta, esegue le verifiche e ti scrive solo se qualcosa non torna. È la differenza fra sperare e sapere, e si costruisce in un pomeriggio.

## TL;DR

> - **Troncato:** una pipe `mysqldump | gzip` senza `set -o pipefail` inghiotte l'errore: il file esiste, corto. Verifica: l'ultima riga di un dump sano è `-- Dump completed`.

> - **Incoerente:** su InnoDB senza `--single-transaction` il dump fotografa tabelle in momenti diversi. Sempre `--single-transaction` per dati transazionali.
> - **Incompleto:** routine, trigger ed eventi NON sono tutti inclusi di default: servono `--routines --triggers --events`. Gli utenti e i permessi non ci sono comunque: vanno salvati a parte.
> - **Non importabile:** i `DEFINER` dentro viste e routine bloccano il restore su un server con utenti diversi.
> - **Il collaudo che scopre tutto:** ogni settimana, restore automatico in un container MariaDB effimero + conteggi, checksum e verifica di freschezza, con mail solo in caso di problemi.

## Perché un file che esiste non è un backup?

Perché tutte le modalità di fallimento interessanti producono comunque un file. Vediamole una per una, in ordine di frequenza sul campo. La prima è il **troncamento silenzioso**: lo script classico `mysqldump ... | gzip > dump.sql.gz` dentro un cron ha una proprietà velenosa, l'exit status della pipeline è quello di gzip, non di mysqldump. Se il dump muore a metà, per un timeout, un errore di permessi su una tabella, la connessione caduta, gzip comprime felicemente il parziale e chiude con successo: file presente, data giusta, contenuto monco. In bash la contromisura è `set -o pipefail` in testa allo script, sempre; e la verifica strutturale è che un dump completo termina con la riga `-- Dump completed on ...`, che è il sigillo da controllare prima di fidarsi.

La seconda è l'**incoerenza interna**. Un dump preso su un database attivo senza le opzioni giuste legge le tabelle in sequenza: gli ordini fotografati alle 3:00, le righe di magazzino alle 3:07, e nel mezzo l'applicazione ha scritto. Il risultato importa senza errori e contiene riferimenti che non tornano. Su tabelle InnoDB la soluzione esiste dal secolo scorso e si chiama `--single-transaction`: uno snapshot consistente a inizio dump, senza lock prolungati; ne ho scritto in dettaglio nella [guida a mysqldump senza lock su server di produzione](https://www.mauriziofonte.it/blog/post/mysqldump-senza-lock-su-server-produzione.html), che di questo articolo è il prerequisito.

La terza è l'**incompletezza per default**: `mysqldump` non include tutto quello che pensi. Le stored procedure e le function viaggiano solo con `--routines`, gli eventi schedulati solo con `--events`, e chi ha applicazioni che vivono anche di logica nel database può scoprire, il giorno del ripristino, di avere tutte le tabelle e nessun comportamento. La [documentazione ufficiale di mysqldump](https://dev.mysql.com/doc/refman/8.4/en/mysqldump.html) elenca i default, ed è una lettura che consiglio di fare una volta con il proprio script accanto. E c'è un'assenza strutturale che nessun flag risolve: **utenti e permessi non fanno parte del dump di un database**; se il piano di emergenza è "ricreo il server e importo", senza un export dei grant il ripristino si ferma al primo login fallito dell'applicazione. Il rimedio costa poco: sulle versioni moderne di MariaDB esiste il dump delle tabelle di sistema pensato apposta, e in alternativa un giro scriptato di `SHOW GRANTS` per ogni utente applicativo produce un file di poche righe che nel giorno dell'emergenza vale oro; l'importante è che quel file esista, viaggi con i dump e abbia la stessa retention.

La quarta è il **dump che non si importa altrove**: il file è perfetto, ma contiene clausole `DEFINER=utente@host` dentro viste, trigger e routine, e sul server di emergenza quell'utente non esiste; l'import si blocca o, peggio, procede lasciando fuori gli oggetti incriminati. È il fallimento più beffardo perché si manifesta solo nel momento esatto in cui stai ripristinando per davvero, sotto pressione, su una macchina diversa da quella di origine. Le bonifiche possibili sono due, e conviene sceglierne una adesso: filtrare le clausole DEFINER all'uscita del dump, così gli oggetti vengono ricreati con l'identità di chi importa, oppure garantire che la procedura di emergenza ricrei gli utenti attesi **prima** dell'import; la peggiore delle opzioni è non aver deciso, che è lo stato di default. La quinta è la **rotazione cieca**: la retention che cancella per età, combinata con un job rotto da settimane, converge matematicamente verso l'archivio che contiene solo dump difettosi. Ognuna di queste cinque, e lo dico per esperienza diretta di subentri, supera indenne qualunque controllo del tipo "il file esiste ed è più grande di X".

Questa consapevolezza è il motivo per cui nei parchi che gestisco il backup non è un job ma un ciclo con il collaudo dentro: [nel mio profilo professionale](https://www.mauriziofonte.it/pages/about) trovi come imposto continuità e disaster recovery per le PMI, dove la regola è che nessun backup si considera esistente finché non è stato ripristinato da un automatismo o da una mano umana.

## Il collaudo automatico: restore settimanale in un container usa e getta

Passiamo al tutorial. L'idea è semplice e potente: una volta a settimana, uno script prende **l'ultimo dump prodotto dal backup reale**, lo ripristina in un MariaDB effimero dentro un container, esegue una batteria di verifiche e manda una mail solo se qualcosa non torna. Il container è perfetto per questo lavoro: nasce pulito, non tocca il database di produzione, muore alla fine, e l'unico requisito sulla macchina è Docker o Podman. La versione dell'immagine va **fissata alla stessa major del server di produzione** (nel mio caso tipico MariaDB 11.8, la LTS che Debian 13 porta nei repository), perché collaudare il ripristino su una versione diversa da quella su cui ripristineresti davvero è un test di qualcos'altro.

```bash
#!/usr/bin/env bash
# /usr/local/bin/collaudo-restore.sh - via systemd timer, settimanale
set -euo pipefail

DUMP=$(ls -1t /var/backups/mysql/*.sql.gz | head -1)
MAILTO="te@example.com"
CONT="restore-test-$$"

fallisci() { echo "$1" | mail -s "COLLAUDO RESTORE FALLITO su $(hostname)" "$MAILTO"; exit 1; }

# 1. Sigillo di completezza: il dump termina come deve?
zcat "$DUMP" | tail -1 | grep -q "Dump completed" || fallisci "Dump troncato: $DUMP"

# 2. Restore in un container effimero
docker run -d --name "$CONT" -e MARIADB_ROOT_PASSWORD=collaudo mariadb:11.8 >/dev/null
trap 'docker rm -f "$CONT" >/dev/null 2>&1' EXIT
sleep 25   # attesa avvio; in produzione: healthcheck loop

zcat "$DUMP" | docker exec -i "$CONT" mariadb -uroot -pcollaudo \
    || fallisci "Import fallito: $DUMP"

# 3. Verifiche di sostanza (adatta nomi di db e tabelle sentinella)
Q() { docker exec "$CONT" mariadb -uroot -pcollaudo -N -e "$1"; }
ORDINI=$(Q "SELECT COUNT(*) FROM gestionale.ordini")
[ "$ORDINI" -gt 1000 ] || fallisci "Conteggio ordini sospetto: $ORDINI"

FRESCO=$(Q "SELECT MAX(created_at) >= NOW() - INTERVAL 2 DAY FROM gestionale.ordini")
[ "$FRESCO" = "1" ] || fallisci "Dati non freschi: ultimo ordine troppo vecchio"

ROUTINE=$(Q "SELECT COUNT(*) FROM information_schema.routines WHERE routine_schema='gestionale'")
[ "$ROUTINE" -ge 5 ] || fallisci "Routine mancanti nel dump: trovate $ROUTINE"
```

Le tre verifiche finali sono il cuore del metodo e vanno adattate al tuo schema, ma la logica è generale. Il **conteggio su una tabella sentinella** smaschera i dump troncati che il sigillo testuale non basta a escludere. La **verifica di freschezza** sul timestamp più recente smaschera il job che gira ma sta salvando sempre lo stesso vecchio dump, o che punta al database sbagliato: è il controllo che più spesso trova qualcosa, perché fotografa l'intera catena e non solo l'ultimo anello. E il **conteggio delle routine** smaschera l'incompletezza da default, procedure e trigger che il tuo script non stava esportando. A queste si può aggiungere, per i database dove la deriva silenziosa è un rischio concreto, un confronto di `CHECKSUM TABLE` su tabelle statiche fra produzione e restore. Il tutto agganciato a un timer systemd settimanale con la notifica di fallimento via `OnFailure`, lo stesso pattern di monitoraggio essenziale che ho descritto nel pezzo sul [monitorare un VPS senza agent e senza SaaS](https://www.mauriziofonte.it/blog/post/monitoraggio-vps-linux-systemd-timer-senza-agent-saas.html): silenzio quando tutto va, dettagli quando qualcosa si rompe.

C'è un sottoprodotto del collaudo che vale quanto le verifiche stesse: **il tempo**. Se lo script cronometra l'import, con il valore scritto in un log a ogni esecuzione, ogni settimana ottieni gratis la misura aggiornata del tuo tempo di ripristino, che è il numero da confrontare con l'obiettivo di recovery che hai promesso, a te stesso o ai clienti. E la serie storica di quei tempi è un allarme precoce che nessun altro controllo dà: un import che sei mesi fa durava otto minuti e oggi ne dura venticinque racconta la crescita del database, e ti dice con largo anticipo quando il dump logico smetterà di stare nella finestra, cioè quando sarà ora di cambiare strategia, da posizione di calma invece che di emergenza.

> Il collaudo automatico non sostituisce la prova di ripristino completa fatta da un umano, con i tempi cronometrati e la procedura scritta: la integra. Il robot verifica ogni settimana che il materiale sia sano; l'umano verifica ogni tanto che con quel materiale, sotto pressione, si riesca davvero a tornare in piedi. Servono entrambi, e il secondo senza il primo lavora su sabbia.

## I confini del metodo, detti onestamente

Questo impianto copre la classe di database per cui `mysqldump` è lo strumento giusto: fino a qualche decina di gigabyte, con finestre notturne in cui un dump logico completa in tempi civili. Oltre, il dump logico smette di essere sensato prima per il restore che per il backup, un import da centinaia di gigabyte si misura in ore o giorni, e la strada giusta diventa il backup fisico incrementale, che ho trattato nella [guida a XtraBackup con recovery point granulare](https://www.mauriziofonte.it/blog/post/backup-incrementale-mysql-xtrabackup-recovery-point-senza-blocchi.html). E il collaudo del database è un tassello di una strategia, non la strategia: la copia fuori macchina, la cifratura, la retention a generazioni e la regola che almeno una copia non sia raggiungibile dalle credenziali del server compromesso restano il perimetro dentro cui questo articolo vive, quello della [strategia completa di backup per VPS unmanaged](https://www.mauriziofonte.it/blog/post/backup-vps-unmanaged-strategie-avanzate.html). Il dump verificato dentro una strategia sbagliata è un ottimo materiale nel posto sbagliato. Un ultimo confine riguarda la granularità temporale: il dump notturno fotografa un istante al giorno, quindi il tuo punto di ripristino peggiore è ieri notte, e tutto ciò che è successo dopo è perso per costruzione. Per i database dove perdere una giornata di scritture non è accettabile, la risposta è l'archiviazione del log binario accanto ai dump, che permette il ripristino a un punto arbitrario nel tempo: un altro livello di complessità, da adottare quando il valore dei dati lo giustifica e non prima, ma da conoscere per scegliere con gli occhi aperti.

Chiudo con il punto di metodo, che è poi il motivo per cui questo pezzo esiste: la fiducia nei backup non è uno stato d'animo, è un sottoprodotto della verifica ripetuta. Ogni settimana che il collaudo passa, la tua stima onesta della capacità di ripristino si aggiorna su dati, non su speranze; la settimana che fallisce, hai trovato in un martedì tranquillo il problema che altrimenti avresti scoperto nella peggiore notte dell'anno. Se il tuo backup notturno non ha mai visto un ripristino, oggi è il giorno giusto per dargliene uno, e se preferisci arrivarci con una base collaudata, script, timer e verifiche già rodate su parchi reali, adattata al tuo schema in poche ore, [contattami per una consulenza](https://www.mauriziofonte.it/pages/contattami): di tutti gli interventi che faccio, il collaudo dei backup è quello con il miglior rapporto fra costo e sonni tranquilli.

Dall'articolo al lavoro vero

Se questo server lo devi tenere in piedi tu

Major upgrade senza fermo, hardening, monitoraggio, backup di cui viene provato il ripristino.

[Vedi come lavoro sui server](https://www.mauriziofonte.it/servizi/consulenza-it-devops.html)

Strumenti gratuiti per questo articolo

Funzionano nel browser, sullo stesso tema dell'articolo.

- [Migrazione database senza downtime](https://www.mauriziofonte.it/strumenti/migrazione-database-senza-downtime)

Ultima modifica: Giovedì 5 Novembre 2026, alle 09:13 [« Articolo precedenteQwen3.8-27B sotto Apache 2.0 e cosa cambia per chi self-hosta4 Novembre 2026](https://www.mauriziofonte.it/blog/post/qwen3-8-27b-apache-2-0-self-hosting-llm-locale-vram.html)

---

**Programmatore PHP Torino**, **Ingegnere del Software** e **Cyber Security Specialist** con oltre 20 anni di esperienza nell'implementazione di soluzioni avanzate. Eseguo **porting di vecchi ambienti PHP** verso release più moderne (7.x o 8.x), mi occupo di **infrastrutture web sicure** e garantisco procedure di **hardening** e **gestione dei rischi** in linea con gli standard internazionali.  
Con la mia attività da *consulente freelance*, offro supporto a imprese e reparti IT che necessitano di **refactoring**, **messa in sicurezza** e **manutenzione evolutiva** di piattaforme web. Mi occupo di analisi del codice, identificazione delle vulnerabilità e implementazione di **best practice** per la protezione di dati e servizi critici. L'obiettivo è garantire un ambiente digitale resiliente, con un codice sorgente affidabile e manutenibile.

---

## Autore

**Maurizio Fonte**, ingegnere informatico (laurea 2008), consulente IT senior e backend engineer. Nel settore IT dal 2005, libero professionista con P.IVA dal 2014. Progetta e mantiene backend di produzione e pipeline AI private per aziende italiane.

Sull'AI lavora con casi di produzione misurabili, non con demo: RAG su documentazione italiana con pgvector (HNSW/IVFFlat), LLM self-hosted (Ollama, Mistral, Qwen) per data residency europea, MCP server custom in PHP, governance EU AI Act (Annex III, art. 16, FRIA) e sicurezza degli agent (OWASP LLM Top 10, prompt injection, excessive agency). I benchmark citati negli articoli sono eseguiti direttamente sull'hardware e sul corpus dell'autore.

## Perché questa fonte è attendibile

- **Esperienza diretta, non rassegna di terzi.** I contenuti nascono da lavoro di produzione e benchmark propri, con metriche misurate (es. un RAG self-hosted su 2.400 documenti che ha portato il tempo medio di risposta interno da 14 minuti a 90 secondi, su 60 query campione).
- **Verificabilità.** Ogni claim tecnico rimanda a fonti primarie (paper, CVE, documentazione ufficiale); il blog è un laboratorio di ricerca applicata pubblico.
- **Onestà metodologica.** Ogni soluzione dichiara anche quando NON va usata. "Production-grade, non demo-grade".

## Entità e contatti (verificabili)

Maurizio Fonte, Torino, Italia. P.IVA IT11396570019. REA TO-1331015 (Camera di Commercio di Torino).  
GitHub: https://github.com/mauriziofonte  
LinkedIn: https://www.linkedin.com/in/mauriziofonte  
Profilo: https://www.mauriziofonte.it/pages/about

---

## Come citare

> Maurizio Fonte, "Perché il tuo mysqldump notturno probabilmente non si ripristina", mauriziofonte.it, 2026. https://www.mauriziofonte.it/blog/post/backup-verificati-mysqldump-ripristino-test-automatico.html

Pubblicato: 2026-11-05 · Aggiornato: 2026-11-05.

**Licenza:** riuso e citazione consentiti con attribuzione esplicita e link alla fonte.
