Backup quotidiano dei database MySQL su Debian, Ubuntu e Linux
Bastano un piccolo script e un cron job per salvare ogni notte tutti i database MySQL e MariaDB in forma compressa. Compresi i punti in cui Debian e Ubuntu divergono e un backup fallisce in silenzio.
Fai girare Debian, Ubuntu o un'altra distribuzione Linux sul tuo VPS, server root o server dedicato e vuoi eseguire ogni giorno il backup automatico di tutti i database MySQL e MariaDB? Sei nel posto giusto. In questa guida configuri uno script di backup che ogni notte esporta tutti i database in un file SQL compresso e ripulisce da solo i backup più vecchi.
La procedura è identica su tutte le distribuzioni e funziona su Debian e Ubuntu esattamente come su AlmaLinux, Rocky Linux e RHEL. Le differenze non stanno nello script, ma nel server di database che gira sulla tua macchina e nel modo in cui gestisce l'accesso. Più avanti trovi una sezione dedicata: Debian a confronto con Ubuntu.
Requisiti
Ti servono un accesso SSH con permessi di root e un server MySQL o MariaDB già installato. Sono consigliate le rispettive versioni attuali delle distribuzioni: Debian 13 "Trixie" e Debian 12 "Bookworm", Ubuntu 24.04 LTS "Noble Numbat" e Ubuntu 22.04 LTS "Jammy Jellyfish", oltre ad AlmaLinux e Rocky Linux nelle versioni 9 e 10. I sistemi più vecchi non andrebbero più usati in produzione: Debian 10 ha raggiunto la fine del supporto a giugno 2024, Ubuntu 20.04 LTS a maggio 2025, e anche CentOS 7 non riceve più aggiornamenti di sicurezza regolari.
Per prima cosa porta il sistema all'ultimo stato e installa l'editor di testo Nano, se ancora manca.
Su Debian e Ubuntu:
apt update && apt upgrade -y
apt install nano -y
Su AlmaLinux, Rocky Linux e RHEL:
dnf update -y
dnf install nano -y
Sui sistemi recenti della famiglia RHEL dnf è il successore di yum. Il vecchio comando di solito funziona ancora come collegamento, ma quello da usare è dnf.
Prevedi inoltre spazio di storage sufficiente. Una settimana di backup compressi occupa in fretta diversi gigabyte, a seconda della dimensione dei database. Lo spazio libero lo controlli con df -h.
Salvare le credenziali in modo sicuro
La password non va scritta direttamente nella chiamata a mysqldump, perché le righe di comando sono visibili a tutti gli utenti del sistema tramite ps. Crea invece un file di credenziali leggibile soltanto da root:
nano /root/.my.cnf
Contenuto del file:
[client]
user=root
password=LA_TUA_PASSWORD_DATABASE
Imposta poi i permessi in modo che solo root abbia accesso:
chmod 600 /root/.my.cnf
Quando non esiste nessuna password di root
Su Debian e Ubuntu l'account di database root di norma non è protetto da una password, ma dall'identità dell'utente di sistema. MariaDB chiama questo metodo unix_socket, MySQL lo chiama auth_socket. In quel caso una password semplicemente non esiste, e una voce nel file di credenziali resta senza effetto. Questa query ti dice se è la tua situazione:
mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"
Se compare unix_socket oppure auth_socket, puoi eseguire lo script come utente di sistema root e rinunciare del tutto al file di credenziali, oppure creare un utente dedicato al backup. La seconda strada è la migliore, perché fa a meno dei permessi completi dell'account root:
CREATE USER 'kh_backup'@'localhost' IDENTIFIED BY 'LA_TUA_PASSWORD_BACKUP';
GRANT SELECT, SHOW VIEW, EVENT, TRIGGER, LOCK TABLES, RELOAD, PROCESS ON *.* TO 'kh_backup'@'localhost';
FLUSH PRIVILEGES;
Nel file di credenziali indichi poi user=kh_backup. I permessi sono volutamente ridotti: lettura, viste, eventi, trigger e, come rete di sicurezza, il blocco delle tabelle che non usano InnoDB. RELOAD e PROCESS si possono assegnare solo a livello globale, da qui ON *.*. PROCESS serve a MySQL 8 per leggere anche le informazioni sui tablespace. Se preferisci non concederlo, aggiungi invece --no-tablespaces nello script. Se non riesci più ad accedere in nessun modo, ti aiuta l'articolo Reimpostare la password di root di MySQL e MariaDB.
Creare lo script di backup
Ora creiamo uno script Bash che si occupa dell'esportazione e cancella i backup vecchi. Nel nostro esempio si chiama mysql_export_all.sh e si trova nella directory /opt/mysqlbackups:
mkdir -p /opt/mysqlbackups
nano /opt/mysqlbackups/mysql_export_all.sh
In questo script scrivi il contenuto seguente:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/mysqlbackups"
KEEP_DAYS=7
DATE=$(date +%Y-%m-%d-%H-%M)
mkdir -p "$BACKUP_DIR"
mysqldump --defaults-extra-file=/root/.my.cnf --all-databases --single-transaction --routines --events | gzip > "$BACKUP_DIR/alldbs_$DATE.sql.gz"
find "$BACKUP_DIR" -type f -name "alldbs_*.sql.gz" -mtime +$KEEP_DAYS -delete
Le opzioni più importanti in sintesi:
--defaults-extra-filelegge utente e password dal file appena creato. Questa opzione deve stare per prima.--single-transactionproduce uno stato coerente delle tabelle InnoDB senza bloccare il database.--routinese--eventsincludono nel backup le stored procedure e gli eventi pianificati. I triggermysqldumpli salva comunque da sé.gzipcomprime l'esportazione e fa risparmiare parecchio storage.- Il comando
findcancella soltanto i file di backup più vecchi di sette giorni. ConKEEP_DAYSadatti la durata di conservazione.
La riga set -euo pipefail conta più di quanto sembri. Senza pipefail la shell valuta solo il valore di ritorno di gzip, che resta zero anche quando mysqldump si è interrotto prima. Ti ritroveresti con un archivio tecnicamente integro ma dal contenuto incompleto, e nessuno se ne accorgerebbe.
Quale server di database gira sulla tua macchina dipende dalla distribuzione. Debian non contiene alcun pacchetto mysql-server e punta interamente su MariaDB, mentre Ubuntu li offre entrambi. Lo script funziona in ogni caso senza modifiche:
| Sistema | mysql-server | mariadb-server |
|---|---|---|
| Debian 13 | non disponibile | 11.8 |
| Debian 12 | non disponibile | 10.11 |
| Ubuntu 24.04 LTS | 8.0 | 10.11 |
| Ubuntu 22.04 LTS | 8.0 | 10.6 |
Rendere lo script eseguibile e provarlo
Rendi lo script eseguibile:
chmod +x /opt/mysqlbackups/mysql_export_all.sh
Eseguilo una volta a mano e controlla il risultato prima di automatizzarlo:
/opt/mysqlbackups/mysql_export_all.sh
ls -lh /opt/mysqlbackups/
Il file creato deve essere nettamente più grande di zero byte. Più dell'inizio, però, conta la fine. Verifica quindi che l'archivio sia intatto e che l'esportazione sia arrivata davvero in fondo:
gzip -t /opt/mysqlbackups/alldbs_*.sql.gz
zcat /opt/mysqlbackups/alldbs_*.sql.gz | tail -n 1
L'ultima riga di un'esportazione completa inizia con -- Dump completed on. Se manca, l'esportazione si è interrotta e il file non vale nulla come backup, anche se pesa diverse centinaia di megabyte. Quella singola riga è il test affidabile più rapido che hai a disposizione.
Configurare il cron job per il backup quotidiano
Apri l'editor dei cron job:
export VISUAL=nano; crontab -e
Per un backup quotidiano alle 5 del mattino aggiungi questa riga:
0 5 * * * /opt/mysqlbackups/mysql_export_all.sh >> /var/log/mysql-backup.log 2>&1
Così l'output finisce in un file di log e puoi ricostruire gli errori in un secondo momento. Se il cron job sia stato salvato correttamente lo verifichi con crontab -l. La voce deve trovarsi nella crontab di root. Su molte immagini Ubuntu l'accesso come root è disattivato, lì il comando è sudo crontab -e. Da utente normale creeresti altrimenti una crontab tua, e lo script si bloccherebbe poi sul file di credenziali sotto /root.
Sulle installazioni minimali della famiglia RHEL a volte manca il servizio cron. Lo installi e lo attivi così:
dnf install cronie -y
systemctl enable --now crond
Da adesso tutti i database vengono esportati ogni notte alle 5, e i backup più vecchi di sette giorni spariscono automaticamente. Trovi altro su pianificazione e trappole tipiche in Configurare un cron job su Linux.
Ripristinare un backup
Un backup vale qualcosa solo quando riesci anche a rimetterlo in produzione. Prova quindi il ripristino una volta di proposito, meglio se su un sistema di test:
zcat /opt/mysqlbackups/alldbs_2026-07-26-05-00.sql.gz | mysql --defaults-extra-file=/root/.my.cnf
Se vuoi recuperare un solo database, decomprimi prima il backup ed estrai la sezione che ti serve, oppure salva in più i singoli database separatamente con mysqldump --databases miodb. È più comodo scrivere subito un file per ogni database. Per farlo, sostituisci nello script la riga mysqldump con questo ciclo:
for DB in $(mysql --defaults-extra-file=/root/.my.cnf -N -B -e "SHOW DATABASES;" | grep -Ev '^(information_schema|performance_schema|sys)$'); do
mysqldump --defaults-extra-file=/root/.my.cnf --single-transaction --routines --events "$DB" | gzip > "$BACKUP_DIR/${DB}_$DATE.sql.gz"
done
I tre database esclusi sono viste sullo stato interno del server e non si possono ripristinare in modo sensato. Adatta anche il pattern di ricerca nel comando find, altrimenti non ripulisce più i nuovi nomi di file. Per il trasferimento completo su un altro server, Trasferire WordPress su un nuovo server mostra la procedura con un esempio pratico.
Conservare i backup fuori dal server
I backup che restano soltanto sullo stesso server non aiutano quando perdi l'intero sistema. Trasferisci quindi i file anche verso una seconda destinazione, per esempio con rsync o scp su un altro server o su uno storage di backup:
rsync -avz /opt/mysqlbackups/ utente@destinazione-backup:/percorso/del/backup/
Aggiungi semplicemente questo comando in fondo al tuo script, così il trasferimento parte in automatico. Perché funzioni nel cron job senza richieste interattive, a root serve una chiave SSH senza passphrase, la cui parte pubblica sia depositata sul sistema di destinazione. Come crearla lo trovi in Collegarsi al server via SSH.
Differenze tra Debian e Ubuntu
Entrambi i sistemi usano apt, entrambi salvano la configurazione sotto /etc/mysql/, e lo script qui sopra gira su tutti e due senza modifiche. Ci sono comunque sei punti in cui divergono, e ognuno di essi può far fallire un backup in silenzio:
| Argomento | Debian | Ubuntu |
|---|---|---|
| Server di database | solo MariaDB | MySQL 8.0 o MariaDB |
| Pacchetto client | mariadb-client | mysql-client-8.0 o mariadb-client |
| Strumento di backup | mariadb-dump, mysqldump come collegamento | con MySQL solo mysqldump |
| Accesso di root | unix_socket | auth_socket con MySQL dal pacchetto |
| Account di manutenzione | da MariaDB 10.4 non viene più creato | debian-sys-maint in /etc/mysql/debian.cnf |
| Log degli errori | journal, journalctl -u mariadb | con MySQL /var/log/mysql/error.log |
Nomi dei pacchetti e strumenti
Se esegui il backup del database da un'altra macchina, lì ti serve solo il pacchetto client. Su Debian si chiama mariadb-client, su Ubuntu con MySQL 8 si chiama mysql-client-8.0. Per uno script che deve girare su entrambi i sistemi esiste il metapacchetto default-mysql-client: su Debian punta al client MariaDB, su Ubuntu al client MySQL.
Da MariaDB 10.5 gli strumenti portano in più un nome con il prefisso mariadb-, da MariaDB 11 quello è il nome vero e proprio e mysqldump è soltanto un collegamento. Lì funzionano entrambe le chiamate. Su un sistema Ubuntu con MySQL 8 esiste invece unicamente mysqldump, e uno script che usa mariadb-dump finisce con command not found. Per gli script che devono girare in entrambi i mondi la chiamata giusta è quindi mysqldump.
Account di manutenzione e autenticazione
Su Ubuntu il pacchetto mysql-server continua a creare l'account di manutenzione debian-sys-maint e ne scrive le credenziali in /etc/mysql/debian.cnf. Questo file è leggibile solo da root e si può usare direttamente per un backup, senza altri preparativi:
mysqldump --defaults-file=/etc/mysql/debian.cnf --all-databases --single-transaction --routines --events | gzip > /opt/mysqlbackups/alldbs.sql.gz
Su Debian con MariaDB dalla versione 10.4 questo account non viene più creato. Il file di solito esiste ancora, ma in genere rimanda semplicemente a root tramite il socket. Non dare quindi per scontato che uno script che su Ubuntu funziona con questo file faccia lo stesso su Debian. Puoi verificarlo in un passaggio:
mysql --defaults-file=/etc/mysql/debian.cnf -e "SELECT current_user();"
MySQL 8 crea i nuovi account con il metodo caching_sha2_password, MariaDB con mysql_native_password. Per l'esportazione tramite il socket locale non fa differenza, ma la fa se esegui il backup dalla rete con un client più vecchio. Se questo segnala "The server requested authentication method unknown to the client", il client è troppo vecchio per MySQL 8 e va aggiornato.
AppArmor su Ubuntu
Su Ubuntu AppArmor è attivo di serie e limita il server di database, cioè il processo mysqld oppure mariadbd. Non limita lo strumento mysqldump, e questa differenza decide se finirai nella trappola oppure no.
L'esportazione descritta in questa guida scrive il file tramite la shell (| gzip > ...), quindi con l'utente che esegue lo script. Questa strada non è toccata da AppArmor e funziona in qualunque directory su cui l'utente possa scrivere. Non appena però è il server stesso a scrivere, valgono altre regole. È il caso di mysqldump --tab=/percorso e di ogni SELECT ... INTO OUTFILE, perché lì il file lo crea il processo del server, non la tua shell. Entrano allora in gioco due blocchi indipendenti tra loro: la variabile di server secure_file_priv e il profilo AppArmor del server. Su Ubuntu la variabile è impostata di serie su /var/lib/mysql-files/, ed è esattamente questa la directory autorizzata anche nel profilo AppArmor. Un percorso di destinazione come /opt/mysqlbackups fallisce quindi due volte.
La parte insidiosa è la diagnosi. Il primo blocco si annuncia chiaramente con ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement. Il secondo compare invece come un semplice Errcode: 13 "Permission denied", anche se proprietario e permessi della directory di destinazione a prima vista sono perfettamente a posto. Chi a quel punto si mette a cambiare i permessi dei file sta cercando nel posto sbagliato. In questo caso controlla entrambe le cose:
mysql -e "SHOW VARIABLES LIKE 'secure_file_priv';"
aa-status | grep -Ei 'mysqld|mariadbd'
journalctl -k | grep -i 'apparmor.*DENIED' | tail -n 20
Se nell'ultimo comando compare una riga con apparmor="DENIED" e il tuo percorso di destinazione, hai trovato la causa. La cosa più semplice è non arrivarci proprio e restare sull'esportazione tramite pipe. Se ti servono davvero scritture lato server, scrivi in /var/lib/mysql-files/ e sposta il file subito dopo. Solo se nessuna delle due strade è praticabile, estendi il profilo con il percorso aggiuntivo. A questo serve il file /etc/apparmor.d/local/usr.sbin.mysqld, con MariaDB corrispondentemente usr.sbin.mariadbd, seguito da systemctl reload apparmor. Disattivare AppArmor non è una soluzione, ma la rimozione di uno strato di protezione che tutela proprio questo server.
Errori frequenti e soluzioni
Il backup è di 0 byte: quasi sempre le credenziali in /root/.my.cnf non sono corrette. Prova l'accesso con mysql --defaults-extra-file=/root/.my.cnf -e "SHOW DATABASES;". Spesso dietro c'è il caso descritto sopra: l'account lavora tramite socket, quindi una password memorizzata non combacia affatto.
"Access denied" nel cron ma non da console: il cron job gira con un utente diverso da quello previsto. Inserisci il job nella crontab di root e usa nello script solo percorsi assoluti. Altre cause le raccoglie Risolvere MySQL Access denied for user.
Unknown table 'COLUMN_STATISTICS' in information_schema: stai salvando un database MariaDB con il mysqldump di MySQL 8, per esempio da una macchina Ubuntu. Durante l'esportazione MySQL 8 interroga una tabella che in MariaDB non esiste. Aggiungi --column-statistics=0 alla chiamata. L'opzione la conosce solo il client MySQL, su un sistema puramente MariaDB non devi impostarla.
Access denied; you need (at least one of) the PROCESS privilege(s) for this operation: sotto MySQL 8 l'utente di backup non può leggere le informazioni sui tablespace. Assegna PROCESS come descritto sopra oppure aggiungi --no-tablespaces.
mariadb-dump: command not found: lo script viene da un sistema Debian con MariaDB 11 e ora gira su Ubuntu con MySQL 8, dove quel nome non esiste. Scrivi mysqldump, che funziona su entrambi i sistemi.
Can't connect to local MySQL server through socket: il server non è in esecuzione, oppure il socket si trova in un percorso diverso da quello previsto. Le possibili cause le descrive Risolvere gli errori di socket MySQL.
SELinux blocca lo script (AlmaLinux, Rocky Linux, RHEL): controlla con ausearch -m avc -ts recent se è stato registrato un rifiuto di accesso e adatta, se necessario, il contesto della directory di backup.
Il disco si riempie: riduci KEEP_DAYS oppure sposta i backup più vecchi su uno storage esterno. Che cos'altro divora spazio lo mostra Liberare spazio su Linux quando il disco è pieno.
Messaggio di errore su --single-transaction con tabelle MyISAM: questa opzione ha effetto solo su InnoDB. Con le tabelle MyISAM puoi usare invece --lock-tables, così le tabelle restano bloccate per tutta la durata dell'esportazione. Tieni presente inoltre che in MariaDB le tabelle di sistema si trovano nel motore Aria e non vengono coperte da --single-transaction.
Se comunque stai già mettendo mano al database, porta a casa anche il resto: rimuovi gli utenti anonimi, cancella il database di test e disattiva l'accesso remoto per root. Come si fa lo trovi in Mettere in sicurezza MariaDB e MySQL.
Domande frequenti
Perché la password del database non deve stare nel comando mysqldump?
Che cosa cambia tra Debian e Ubuntu nel backup dei database MySQL?
Su Ubuntu AppArmor impedisce la scrittura del file di backup. Che cosa faccio?
La guida funziona su qualsiasi distribuzione Linux?
Che cosa faccio se l'account di database root non ha nessuna password?
Per quanto tempo vengono conservati i backup?
Basta conservare i backup sullo stesso server?
Come ripristino un backup?
Il cron job non parte oppure segnala Access denied. Che cosa posso controllare?
Il backup è di 0 byte. Da che cosa dipende?
2024-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.

