MySQL-Datenbanken unter Debian, Ubuntu und Linux täglich sichern
Ein kleines Skript plus Cronjob genügt, um alle MySQL- und MariaDB-Datenbanken jede Nacht komprimiert zu sichern. Inklusive der Stellen, an denen Debian und Ubuntu sich unterscheiden und ein Backup lautlos scheitert.
Sie betreiben Debian, Ubuntu oder eine andere Linux-Distribution auf Ihrem vServer, Rootserver oder dedizierten Server und möchten alle Ihre MySQL- und MariaDB-Datenbanken automatisch täglich sichern? Dann sind Sie hier genau richtig. In dieser Anleitung richten Sie ein Backup-Skript ein, das jede Nacht alle Datenbanken als komprimierte SQL-Datei exportiert und alte Sicherungen automatisch aufräumt.
Der Ablauf ist auf allen Distributionen derselbe und funktioniert unter Debian und Ubuntu ebenso wie unter AlmaLinux, Rocky Linux und RHEL. Die Unterschiede stecken nicht im Skript, sondern darin, welcher Datenbankserver bei Ihnen läuft und wie er sich anmelden lässt. Dafür gibt es weiter unten einen eigenen Abschnitt zu Debian gegenüber Ubuntu.
Voraussetzungen
Sie benötigen einen SSH-Zugang mit Root-Rechten und einen installierten MySQL- oder MariaDB-Server. Empfohlen sind die jeweils aktuellen Distributionsversionen: Debian 13 "Trixie" und Debian 12 "Bookworm", Ubuntu 24.04 LTS "Noble Numbat" und Ubuntu 22.04 LTS "Jammy Jellyfish" sowie AlmaLinux und Rocky Linux in den Versionen 9 und 10. Ältere Systeme sollten Sie nicht mehr produktiv einsetzen: Debian 10 hat im Juni 2024 sein Supportende erreicht, Ubuntu 20.04 LTS im Mai 2025, und CentOS 7 erhält ebenfalls keine regulären Sicherheitsupdates mehr.
Bringen Sie das System zunächst auf den aktuellen Stand und installieren Sie den Texteditor Nano, falls er noch fehlt.
Für Debian und Ubuntu:
apt update && apt upgrade -y
apt install nano -y
Für AlmaLinux, Rocky Linux und RHEL:
dnf update -y
dnf install nano -y
Auf aktuellen Systemen der RHEL-Familie ist dnf der Nachfolger von yum. Der alte Befehl funktioniert dort meist noch als Verweis, verwenden sollten Sie jedoch dnf.
Planen Sie außerdem ausreichend Speicherplatz ein. Eine Woche komprimierter Sicherungen belegt je nach Datenbankgröße schnell mehrere Gigabyte. Den freien Platz prüfen Sie mit df -h.
Zugangsdaten sicher hinterlegen
Das Passwort gehört nicht direkt in den Aufruf von mysqldump, denn Kommandozeilen sind über ps für alle Benutzer des Systems sichtbar. Legen Sie stattdessen eine Zugangsdatei an, die nur root lesen darf:
nano /root/.my.cnf
Inhalt der Datei:
[client]
user=root
password=IHR_DATENBANK_PASSWORT
Setzen Sie anschließend die Rechte so, dass ausschließlich root Zugriff hat:
chmod 600 /root/.my.cnf
Wenn es gar kein Root-Passwort gibt
Auf Debian und Ubuntu ist das Datenbankkonto root in der Regel nicht über ein Passwort abgesichert, sondern über die Identität des Systembenutzers. MariaDB nennt das Verfahren unix_socket, MySQL nennt es auth_socket. Ein Passwort existiert dann schlicht nicht, und ein Eintrag in der Zugangsdatei läuft ins Leere. Ob das bei Ihnen der Fall ist, zeigt diese Abfrage:
mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"
Steht dort unix_socket oder auth_socket, lassen Sie das Skript entweder als Systembenutzer root laufen und verzichten ganz auf die Zugangsdatei, oder Sie legen einen eigenen Backup-Benutzer an. Die zweite Variante ist die bessere, weil sie ohne die vollen Rechte des Root-Kontos auskommt:
CREATE USER 'kh_backup'@'localhost' IDENTIFIED BY 'IHR_BACKUP_PASSWORT';
GRANT SELECT, SHOW VIEW, EVENT, TRIGGER, LOCK TABLES, RELOAD, PROCESS ON *.* TO 'kh_backup'@'localhost';
FLUSH PRIVILEGES;
In der Zugangsdatei tragen Sie dann user=kh_backup ein. Die Rechte sind bewusst knapp gehalten: Lesen, Sichten, Events, Trigger und als Rückfallebene das Sperren von Tabellen, die nicht InnoDB nutzen. RELOAD und PROCESS lassen sich nur global vergeben, daher ON *.*. PROCESS braucht MySQL 8, um die Tablespace-Informationen mitzulesen. Wer es nicht vergeben möchte, ergänzt im Skript stattdessen --no-tablespaces. Kommen Sie überhaupt nicht mehr hinein, hilft der Beitrag MySQL- und MariaDB-Root-Passwort zurücksetzen.
Backup-Skript erstellen
Nun erstellen wir ein Bash-Skript, das den Export übernimmt und alte Sicherungen löscht. In unserem Beispiel heißt es mysql_export_all.sh und liegt im Verzeichnis /opt/mysqlbackups:
mkdir -p /opt/mysqlbackups
nano /opt/mysqlbackups/mysql_export_all.sh
In dieses Skript schreiben Sie folgenden Inhalt:
#!/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
Die wichtigsten Optionen im Überblick:
--defaults-extra-fileliest Benutzer und Passwort aus der eben angelegten Datei. Diese Option muss als erste Option stehen.--single-transactionerzeugt bei InnoDB-Tabellen einen in sich konsistenten Stand, ohne die Datenbank zu sperren.--routinesund--eventsnehmen gespeicherte Prozeduren und geplante Events mit ins Backup. Trigger sichertmysqldumpohnehin von sich aus.gzipkomprimiert den Export und spart dadurch deutlich Speicherplatz.- Der
find-Befehl löscht ausschließlich Backup-Dateien, die älter als sieben Tage sind. ÜberKEEP_DAYSpassen Sie die Aufbewahrungsdauer an.
Die Zeile set -euo pipefail ist wichtiger, als sie aussieht. Ohne pipefail bewertet die Shell nur den Rückgabewert von gzip, und der ist auch dann null, wenn mysqldump vorher abgebrochen ist. Sie hätten dann eine technisch fehlerfreie Archivdatei mit unvollständigem Inhalt, und niemand würde es bemerken.
Welcher Datenbankserver bei Ihnen läuft, hängt von der Distribution ab. Debian enthält kein Paket mysql-server und setzt durchgehend auf MariaDB, Ubuntu bietet dagegen beides an. Das Skript funktioniert in allen Fällen unverändert:
| System | mysql-server | mariadb-server |
|---|---|---|
| Debian 13 | nicht vorhanden | 11.8 |
| Debian 12 | nicht vorhanden | 10.11 |
| Ubuntu 24.04 LTS | 8.0 | 10.11 |
| Ubuntu 22.04 LTS | 8.0 | 10.6 |
Skript ausführbar machen und testen
Machen Sie das Skript ausführbar:
chmod +x /opt/mysqlbackups/mysql_export_all.sh
Führen Sie es einmal von Hand aus und prüfen Sie das Ergebnis, bevor Sie es automatisieren:
/opt/mysqlbackups/mysql_export_all.sh
ls -lh /opt/mysqlbackups/
Die erzeugte Datei sollte deutlich größer als null Byte sein. Aussagekräftiger als der Anfang ist aber das Ende. Prüfen Sie deshalb, ob das Archiv unbeschädigt und der Export wirklich durchgelaufen ist:
gzip -t /opt/mysqlbackups/alldbs_*.sql.gz
zcat /opt/mysqlbackups/alldbs_*.sql.gz | tail -n 1
Die letzte Zeile eines vollständigen Exports beginnt mit -- Dump completed on. Fehlt sie, wurde der Export abgebrochen, und die Datei ist als Sicherung wertlos, auch wenn sie mehrere hundert Megabyte groß ist. Diese eine Zeile ist der schnellste verlässliche Test, den Sie haben.
Cronjob für das tägliche Backup einrichten
Öffnen Sie den Cronjob-Editor:
export VISUAL=nano; crontab -e
Für ein tägliches Backup um 5 Uhr morgens ergänzen Sie folgende Zeile:
0 5 * * * /opt/mysqlbackups/mysql_export_all.sh >> /var/log/mysql-backup.log 2>&1
Die Ausgabe landet damit in einer Logdatei, sodass Sie Fehler später nachvollziehen können. Ob der Cronjob korrekt eingetragen wurde, prüfen Sie mit crontab -l. Der Job muss dabei in der Crontab von root stehen. Auf vielen Ubuntu-Abbildern ist die Anmeldung als root deaktiviert, dort lautet der Aufruf sudo crontab -e. Als normaler Benutzer legen Sie sonst eine eigene Crontab an, und das Skript scheitert später an der Zugangsdatei unter /root.
Auf minimalen Installationen der RHEL-Familie fehlt der Cron-Dienst gelegentlich. Sie installieren und aktivieren ihn so:
dnf install cronie -y
systemctl enable --now crond
Ab jetzt werden alle Datenbanken jede Nacht um 5 Uhr exportiert, und Sicherungen, die älter als sieben Tage sind, verschwinden automatisch. Mehr zu Zeitplänen und typischen Stolpersteinen steht in Cronjob unter Linux einrichten.
Backup zurückspielen
Ein Backup ist erst dann etwas wert, wenn Sie es auch einspielen können. Testen Sie die Wiederherstellung deshalb einmal bewusst, am besten auf einem Testsystem:
zcat /opt/mysqlbackups/alldbs_2026-07-26-05-00.sql.gz | mysql --defaults-extra-file=/root/.my.cnf
Möchten Sie nur eine einzelne Datenbank zurückholen, entpacken Sie das Backup zuerst und extrahieren den passenden Abschnitt, oder Sie sichern einzelne Datenbanken zusätzlich separat mit mysqldump --databases meinedb. Bequemer ist es, gleich je Datenbank eine eigene Datei zu schreiben. Ersetzen Sie dafür im Skript die mysqldump-Zeile durch diese Schleife:
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
Die drei ausgeschlossenen Datenbanken sind Sichten auf interne Serverzustände und lassen sich nicht sinnvoll zurückspielen. Passen Sie auch das Suchmuster im find-Befehl an, sonst räumt es die neuen Dateinamen nicht mehr auf. Für den kompletten Umzug auf einen anderen Server zeigt WordPress auf einen neuen Server umziehen den Ablauf an einem praktischen Beispiel.
Backups außerhalb des Servers ablegen
Sicherungen, die ausschließlich auf demselben Server liegen, helfen bei einem Datenverlust des gesamten Systems nicht weiter. Übertragen Sie die Dateien deshalb zusätzlich auf ein zweites Ziel, etwa per rsync oder scp auf einen anderen Server oder einen Backup-Speicher:
rsync -avz /opt/mysqlbackups/ benutzer@backupziel:/pfad/zum/backup/
Ergänzen Sie diesen Befehl einfach am Ende Ihres Skripts, dann läuft die Übertragung automatisch mit. Damit das im Cronjob ohne Rückfrage funktioniert, braucht root einen SSH-Schlüssel ohne Passphrase, dessen öffentlicher Teil auf dem Zielsystem hinterlegt ist. Wie Sie den anlegen, steht in Per SSH mit dem Server verbinden.
Unterschiede zwischen Debian und Ubuntu
Beide Systeme nutzen apt, beide legen die Konfiguration unter /etc/mysql/ ab, und das Skript oben läuft auf beiden unverändert. Trotzdem gibt es sechs Punkte, an denen sie sich unterscheiden, und jeder einzelne davon kann ein Backup lautlos scheitern lassen:
| Thema | Debian | Ubuntu |
|---|---|---|
| Datenbankserver | ausschließlich MariaDB | MySQL 8.0 oder MariaDB |
| Client-Paket | mariadb-client | mysql-client-8.0 oder mariadb-client |
| Sicherungswerkzeug | mariadb-dump, mysqldump als Verweis | bei MySQL nur mysqldump |
| Anmeldung von root | unix_socket | auth_socket bei MySQL aus dem Paket |
| Wartungskonto | ab MariaDB 10.4 nicht mehr angelegt | debian-sys-maint in /etc/mysql/debian.cnf |
| Fehlerlog | Journal, journalctl -u mariadb | bei MySQL /var/log/mysql/error.log |
Paketnamen und Werkzeuge
Wenn Sie die Datenbank von einem anderen Rechner aus sichern, brauchen Sie dort nur das Client-Paket. Unter Debian heißt es mariadb-client, unter Ubuntu mit MySQL 8 heißt es mysql-client-8.0. Für ein Skript, das auf beiden Systemen laufen soll, gibt es das Metapaket default-mysql-client: Es zeigt unter Debian auf den MariaDB-Client und unter Ubuntu auf den MySQL-Client.
Ab MariaDB 10.5 tragen die Werkzeuge zusätzlich einen Namen mit dem Präfix mariadb-, ab MariaDB 11 ist dieser Name der eigentliche und mysqldump nur noch ein Verweis darauf. Beide Aufrufe funktionieren dort. Auf einem Ubuntu-System mit MySQL 8 gibt es dagegen ausschließlich mysqldump, ein Skript mit mariadb-dump endet dort mit command not found. Für Skripte, die auf beiden Welten laufen sollen, ist mysqldump deshalb der richtige Aufruf.
Wartungskonto und Authentifizierung
Auf Ubuntu legt das Paket mysql-server weiterhin das Wartungskonto debian-sys-maint an und schreibt dessen Zugangsdaten nach /etc/mysql/debian.cnf. Diese Datei ist nur für root lesbar und lässt sich ohne weitere Vorbereitung direkt für ein Backup verwenden:
mysqldump --defaults-file=/etc/mysql/debian.cnf --all-databases --single-transaction --routines --events | gzip > /opt/mysqlbackups/alldbs.sql.gz
Unter Debian mit MariaDB ab Version 10.4 wird dieses Konto nicht mehr neu angelegt. Die Datei existiert dort zwar meist noch, verweist aber in der Regel nur auf root über den Socket. Verlassen Sie sich also nicht darauf, dass ein Skript, das unter Ubuntu über diese Datei läuft, auf Debian dasselbe tut. Prüfen können Sie es in einem Schritt:
mysql --defaults-file=/etc/mysql/debian.cnf -e "SELECT current_user();"
Neue Konten legt MySQL 8 mit dem Verfahren caching_sha2_password an, MariaDB mit mysql_native_password. Für den Export über den lokalen Socket spielt das keine Rolle, wohl aber, wenn Sie von einem älteren Client aus über das Netzwerk sichern. Meldet dieser "The server requested authentication method unknown to the client", ist der Client zu alt für MySQL 8 und gehört aktualisiert.
AppArmor unter Ubuntu
Unter Ubuntu ist AppArmor ab Werk aktiv und schränkt den Datenbankserver ein, also den Prozess mysqld beziehungsweise mariadbd. Es schränkt nicht das Werkzeug mysqldump ein, und dieser Unterschied entscheidet darüber, ob Ihnen die Falle begegnet.
Der Export aus dieser Anleitung schreibt die Datei über die Shell (| gzip > ...), also unter dem Benutzer, der das Skript ausführt. Dieser Weg ist von AppArmor nicht betroffen und funktioniert in jedem Verzeichnis, auf das der Benutzer schreiben darf. Sobald jedoch der Server selbst schreibt, gelten andere Regeln. Das ist bei mysqldump --tab=/pfad der Fall und bei jedem SELECT ... INTO OUTFILE, denn dabei erzeugt der Serverprozess die Datei, nicht Ihre Shell. Dann greifen zwei voneinander unabhängige Sperren: die Servervariable secure_file_priv und das AppArmor-Profil des Servers. Unter Ubuntu steht die Variable ab Werk auf /var/lib/mysql-files/, und genau dieses Verzeichnis ist auch im AppArmor-Profil freigegeben. Ein Zielpfad wie /opt/mysqlbackups scheitert deshalb doppelt.
Tückisch ist dabei die Fehlersuche. Die erste Sperre meldet sich deutlich mit ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement. Die zweite erscheint dagegen als schlichtes Errcode: 13 "Permission denied", obwohl Eigentümer und Rechte des Zielverzeichnisses auf den ersten Blick völlig in Ordnung sind. Wer dann Dateirechte vergibt, sucht an der falschen Stelle. Prüfen Sie in diesem Fall beides:
mysql -e "SHOW VARIABLES LIKE 'secure_file_priv';"
aa-status | grep -Ei 'mysqld|mariadbd'
journalctl -k | grep -i 'apparmor.*DENIED' | tail -n 20
Taucht im letzten Befehl eine Zeile mit apparmor="DENIED" und Ihrem Zielpfad auf, haben Sie die Ursache gefunden. Am einfachsten lassen Sie es gar nicht darauf ankommen und bleiben beim Export über die Pipe. Brauchen Sie serverseitige Schreibvorgänge wirklich, schreiben Sie nach /var/lib/mysql-files/ und verschieben die Datei anschließend. Nur wenn beides ausscheidet, erweitern Sie das Profil um den zusätzlichen Pfad. Dafür ist die Datei /etc/apparmor.d/local/usr.sbin.mysqld vorgesehen, bei MariaDB entsprechend usr.sbin.mariadbd, gefolgt von systemctl reload apparmor. AppArmor abzuschalten ist keine Lösung, sondern das Entfernen einer Schutzschicht, die genau diesen Server absichert.
Häufige Fehler und Lösungen
Das Backup ist 0 Byte groß: Meist stimmen die Zugangsdaten in /root/.my.cnf nicht. Testen Sie die Anmeldung mit mysql --defaults-extra-file=/root/.my.cnf -e "SHOW DATABASES;". Häufig steckt dahinter der oben beschriebene Fall, dass das Konto über den Socket arbeitet und ein hinterlegtes Passwort deshalb gar nicht passt.
"Access denied" im Cron, aber nicht auf der Konsole: Der Cronjob läuft unter einem anderen Benutzer als erwartet. Tragen Sie den Job in die Crontab von root ein und nutzen Sie im Skript ausschließlich absolute Pfade. Weitere Ursachen sammelt MySQL Access denied for user beheben.
Unknown table 'COLUMN_STATISTICS' in information_schema: Sie sichern eine MariaDB-Datenbank mit dem mysqldump aus MySQL 8, etwa von einem Ubuntu-Rechner aus. MySQL 8 fragt beim Export eine Tabelle ab, die es in MariaDB nicht gibt. Hängen Sie --column-statistics=0 an den Aufruf an. Die Option kennt nur der MySQL-Client, auf einem reinen MariaDB-System dürfen Sie sie nicht setzen.
Access denied; you need (at least one of) the PROCESS privilege(s) for this operation: Der Backup-Benutzer darf unter MySQL 8 die Tablespace-Informationen nicht lesen. Vergeben Sie PROCESS wie oben beschrieben oder ergänzen Sie --no-tablespaces.
mariadb-dump: command not found: Das Skript stammt von einem Debian-System mit MariaDB 11 und läuft nun auf Ubuntu mit MySQL 8. Dort gibt es diesen Namen nicht. Schreiben Sie mysqldump, das funktioniert auf beiden Systemen.
Can't connect to local MySQL server through socket: Der Server läuft nicht, oder der Socket liegt an einem anderen Pfad als erwartet. Die möglichen Ursachen beschreibt MySQL-Socket-Fehler beheben.
SELinux blockiert das Skript (AlmaLinux, Rocky Linux, RHEL): Prüfen Sie mit ausearch -m avc -ts recent, ob eine Zugriffsverweigerung protokolliert wurde, und passen Sie den Kontext des Backup-Verzeichnisses gegebenenfalls an.
Die Festplatte läuft voll: Reduzieren Sie KEEP_DAYS, oder verschieben Sie ältere Sicherungen auf einen externen Speicher. Was sonst noch Platz frisst, zeigt Festplatte voll unter Linux aufräumen.
Fehlermeldung zu --single-transaction bei MyISAM-Tabellen: Diese Option wirkt nur bei InnoDB. Bei MyISAM-Tabellen können Sie stattdessen --lock-tables verwenden, dann werden die Tabellen für die Dauer des Exports gesperrt. Beachten Sie außerdem, dass die Systemtabellen bei MariaDB in der Aria-Engine liegen und von --single-transaction nicht erfasst werden.
Wenn Sie ohnehin gerade an der Datenbank arbeiten, nehmen Sie den Rest gleich mit: anonyme Benutzer entfernen, die Testdatenbank löschen und den Fernzugriff für root abschalten. Wie das geht, steht in MariaDB und MySQL absichern.
Häufige Fragen
Warum sollte das Datenbank-Passwort nicht im mysqldump-Befehl stehen?
Was unterscheidet Debian und Ubuntu beim Sichern von MySQL-Datenbanken?
AppArmor verhindert unter Ubuntu das Schreiben der Backup-Datei. Was tun?
Funktioniert die Anleitung auf jeder Linux-Distribution?
Was mache ich, wenn das Datenbank-Konto root gar kein Passwort hat?
Wie lange werden die Backups aufbewahrt?
Reicht es, die Backups auf demselben Server zu speichern?
Wie spiele ich ein Backup wieder ein?
Der Cronjob läuft nicht oder meldet Access denied. Was kann ich prüfen?
Das Backup ist 0 Byte groß. Woran liegt das?
2024-2026 KernelHost GmbH. Alle Rechte vorbehalten. Diese Anleitung ist urheberrechtlich geschützt. Eine Veröffentlichung auf anderen Webseiten, auch auszugsweise oder in bearbeiteter Form, ist ohne unsere schriftliche Zustimmung nicht gestattet. Zitate mit Quellenangabe und Link sind ausdrücklich willkommen.

