MySQL-Fehler "Access denied for user" beheben
Warum "Access denied for user" meist nichts mit einem falschen Passwort zu tun hat: unix_socket, localhost gegen 127.0.0.1, Rechte und der sichere Root-Passwort-Reset.
"Access denied for user" ist die meistgesuchte Fehlermeldung im MySQL-Umfeld, und in den meisten Fällen ist das Passwort gar nicht schuld. Auf Debian und Ubuntu scheitert die Anmeldung überwiegend an der Socket-Authentifizierung, an der Verwechslung von localhost mit 127.0.0.1 oder an einer Benutzerzeile, die für den falschen Host angelegt wurde. Dieser Artikel geht die Ursachen in der Reihenfolge durch, in der sie tatsächlich auftreten, zeigt den Passwort-Reset über skip-grant-tables samt Rückweg, und beschreibt, woran Sie erkennen, dass die Anmeldung wirklich repariert ist.
Alle Angaben sind auf Debian 13 (MariaDB 11.8), Debian 12 (MariaDB 10.11), Ubuntu 24.04 (MariaDB 10.11 oder MySQL 8.0) und Ubuntu 22.04 (MariaDB 10.6 oder MySQL 8.0) bezogen. Ein wichtiger Punkt vorweg: Debian liefert überhaupt kein Paket mysql-server. Wenn Sie auf einem Debian-System "MySQL" installiert haben, läuft dort MariaDB, und das erklärt bereits einen Teil der Verwirrung.
Die Fehlermeldung genau lesen
Der Wortlaut entscheidet, welche Ursache infrage kommt. Diese fünf Varianten begegnen Ihnen in der Praxis:
| Meldung | Bedeutung |
|---|---|
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) | Ein Passwort wurde gesendet und passte nicht, oder es gibt keine passende Kontozeile. |
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) | Es wurde gar kein Passwort gesendet. Meist fehlt -p oder die Anwendung liest die Konfiguration nicht. |
ERROR 1698 (28000): Access denied for user 'root'@'localhost' | Der Klassiker: das Konto nutzt unix_socket beziehungsweise auth_socket. Ein Passwort ist hier bedeutungslos. |
ERROR 1044 (42000): Access denied for user 'app'@'localhost' to database 'shop' | Die Anmeldung war erfolgreich. Es fehlen nur Rechte auf dieser Datenbank. |
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/run/mysqld/mysqld.sock' | Kein Authentifizierungsproblem. Der Dienst läuft nicht oder der Socket-Pfad stimmt nicht. |
Zwei Details werden regelmäßig überlesen. Erstens ist der Hostname in der Meldung der Host, als den der Server Sie gesehen hat, nicht der, den Sie eingetippt haben. Steht dort 'app'@'localhost', obwohl Sie sich mit -h 127.0.0.1 verbunden haben, dann hat der Server eine Rückwärtsauflösung durchgeführt. Zweitens unterscheidet 1045 nicht zwischen "Passwort falsch" und "Konto existiert nicht". Beides ergibt dieselbe Ausgabe, absichtlich, damit ein Angreifer keine gültigen Benutzernamen durchprobieren kann.
Der häufigste Fall: unix_socket bei MariaDB
Seit MariaDB 10.4 legen die Debian- und Ubuntu-Pakete das Konto root@localhost so an, dass es über den Unix-Socket authentifiziert wird. Die Kontozeile sieht sinngemäß so aus:
CREATE USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING 'invalid'
OR unix_socket;
Das bedeutet: Wer als Systembenutzer root angemeldet ist, kommt ohne Passwort hinein. Wer ein Passwort schickt, wird gegen den Hash der Zeichenkette "invalid" geprüft, und den kann niemand treffen. Der Teil mit mysql_native_password steht nur deshalb da, weil SET PASSWORD sonst mit einem Fehler abbrechen würde. Praktische Folge: mysql -u root -p als normaler Benutzer scheitert unabhängig davon, welches Passwort Sie eingeben. Richtig ist:
sudo mariadb
Unter MySQL 8.0 auf Ubuntu heißt das Plugin auth_socket statt unix_socket, das Verhalten ist identisch. Der Aufruf lautet dort sudo mysql.
Prüfen Sie, welches Plugin ein Konto tatsächlich nutzt, und zwar mit SHOW CREATE USER:
mariadb -e "SHOW CREATE USER 'root'@'localhost';"
Die Ausgabe zeigt die vollständige Kette beider Methoden:
CREATE USER `root`@`localhost` IDENTIFIED VIA mysql_native_password USING 'invalid' OR unix_socket
Hier lauert eine Falle, die kaum eine Anleitung erwähnt: seit MariaDB 10.4 ist mysql.user nur noch eine Sicht (View), die echten Daten liegen als JSON in mysql.global_priv. Diese Sicht kennt pro Konto nur eine einzige Authentifizierungsmethode und meldet für root@localhost deshalb mysql_native_password, obwohl in Wirklichkeit unix_socket greift. Wer die verbreitete Abfrage SELECT User, Host, plugin FROM mysql.user als Beleg nimmt, hält den Server für passwortauthentifiziert und sucht den Fehler an der falschen Stelle. Denselben blinden Fleck hat JSON_VALUE(priv,"$.plugin"), denn auch das liest nur das erste Element. Die zweite Methode steht im Feld auth_or und muss eigens ausgelesen werden:
mariadb -e 'SELECT CONCAT(user,"@",host) AS konto, JSON_VALUE(priv,"$.plugin") AS plugin, JSON_QUERY(priv,"$.auth_or") AS auth_or FROM mysql.global_priv;'
Für root@localhost steht in der Spalte plugin dann weiterhin mysql_native_password, in auth_or dagegen [{},{"plugin":"unix_socket"}]. Erst diese zweite Spalte zeigt, dass die Socket-Anmeldung aktiv ist.
Ist das Plugin geladen? Bei einem defekten Upgrade kann es fehlen, dann meldet der Server ERROR 1524 (HY000): Plugin 'unix_socket' is not loaded:
mariadb -e "SELECT plugin_name, plugin_status FROM information_schema.plugins WHERE plugin_name LIKE '%socket%';"
Wenn Sie root bewusst auf ein Passwort umstellen wollen, etwa weil ein Backup-Skript unter einem anderen Systembenutzer läuft, dann behalten Sie die Socket-Variante zusätzlich bei. Andernfalls funktionieren Wartungsskripte der Distribution und sudo mariadb nicht mehr:
ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket
OR mysql_native_password USING PASSWORD('IhrNeuesPasswort');
Der bessere Weg ist ohnehin ein eigenes Administrationskonto statt eines Passworts für root. Für Anwendungen gilt das erst recht: eine Datenbank, ein Benutzer, nur die nötigen Rechte.
localhost ist nicht 127.0.0.1
Diese Unterscheidung verursacht mehr "Access denied"-Meldungen als jedes falsche Passwort. Die Clients von MySQL und MariaDB behandeln den Hostnamen localhost als Sonderfall und verbinden sich über den Unix-Socket. Erst 127.0.0.1 erzwingt eine TCP-Verbindung. Beides sind aus Sicht der Rechteverwaltung dieselben Rechnernamen nicht, denn der Server trägt bei einer Socket-Verbindung den Host localhost ein, bei TCP über die Loopback-Adresse entweder localhost (nach Rückwärtsauflösung) oder 127.0.0.1, je nach Einstellung von skip_name_resolve.
Ein Konto, das nur als 'app'@'127.0.0.1' existiert, ist über den Socket nicht erreichbar, und umgekehrt. Genau das passiert, wenn eine PHP-Anwendung host=localhost in der Konfiguration stehen hat: PHP verbindet sich über den Socket, die Rechte wurden aber für die IP vergeben. Zwei Gegenproben:
mariadb -u app -p'Passwort' -e "SELECT USER(), CURRENT_USER();"
mariadb -h 127.0.0.1 -u app -p'Passwort' -e "SELECT USER(), CURRENT_USER();"
Schlägt nur eine der beiden fehl, ist die Ursache gefunden. Achten Sie dabei auf die Namensauflösung: solange skip_name_resolve ausgeschaltet ist, und das ist es in den Paketinstallationen von Debian und Ubuntu, löst der Server die Loopback-Adresse rückwärts nach localhost auf. Existiert bereits ein Konto 'app'@'localhost', gelingt deshalb auch die Anmeldung mit -h 127.0.0.1, und CURRENT_USER() meldet dann app@localhost statt app@127.0.0.1. Ein zusätzlich angelegtes Konto 'app'@'127.0.0.1' bleibt in diesem Fall wirkungslos, es greift erst, wenn das localhost-Konto fehlt. Die saubere Lösung ist, das Konto für den Weg anzulegen, den die Anwendung tatsächlich nimmt, und nicht beide Varianten "für alle Fälle" einzurichten.
Prüfen Sie zusätzlich, ob die Namensauflösung abgeschaltet ist. Ist skip_name_resolve aktiv, funktionieren Kontozeilen mit Hostnamen wie 'app'@'web01.intern' überhaupt nicht mehr, es zählen dann nur noch IP-Adressen:
mariadb -e "SHOW VARIABLES LIKE 'skip_name_resolve';"
Ein weiterer Stolperstein ist die Reihenfolge, in der der Server passende Zeilen auswählt. Er sortiert von spezifisch nach allgemein und nimmt den ersten Treffer, nicht den besten. Existiert neben 'app'@'%' noch ein anonymes Konto ''@'localhost', gewinnt bei einer lokalen Verbindung das anonyme Konto, und Ihre Anmeldung scheitert mit einer Meldung, die trotzdem Ihren Benutzernamen nennt. Aktuelle Pakete legen keine anonymen Konten mehr an, auf über Jahre migrierten Systemen findet man sie aber noch:
mariadb -e "SELECT user, host FROM mysql.global_priv WHERE user = '';"
Passwort, Rechte und Großschreibung
Bleiben die Ursachen, die tatsächlich mit den Zugangsdaten zu tun haben.
Die Shell frisst Sonderzeichen
Zwischen -p und dem Passwort darf kein Leerzeichen stehen, sonst interpretiert der Client das Passwort als Datenbanknamen. Passwörter mit $, !, & oder Leerzeichen gehören in einfache Anführungszeichen, weil die Shell sie sonst ersetzt oder abschneidet. Wirklich sauber ist es, das Passwort gar nicht auf der Kommandozeile zu übergeben, denn dort landet es in der Prozessliste und in der Verlaufsdatei. Legen Sie stattdessen eine Datei ~/.my.cnf mit Rechten 0600 an:
[client]
user=app
password=IhrPasswort
Umgekehrt ist eine vergessene ~/.my.cnf auch eine mögliche Fehlerursache. Sie überschreibt stillschweigend, was Sie auf der Kommandozeile angeben, und dann bekommen Sie "Access denied" für einen Benutzer, den Sie nie eingetippt haben.
Großschreibung: Benutzername ja, Host nein
Benutzernamen sind in MySQL und MariaDB zeichenweise exakt zu treffen, App und app sind zwei verschiedene Konten. Hostnamen werden beim Vergleich ohne Rücksicht auf Groß- und Kleinschreibung geprüft, aber genau so gespeichert, wie sie im CREATE USER standen. Wer versehentlich 'app'@'LOCALHOST' angelegt hat, sieht in SHOW GRANTS-Ausgaben und Skripten zwei scheinbar unterschiedliche Konten, die aber auf dieselbe Verbindung passen. Solche Duplikate machen die Fehlersuche unnötig zäh, weil Sie Rechte auf der einen Zeile vergeben und der Server die andere auswählt. Aufspüren lassen sie sich so:
mariadb -e "SELECT user, host FROM mysql.global_priv WHERE host <> LOWER(host);"
Rechte fehlen, nicht die Anmeldung
Kommt ERROR 1044 statt 1045, war die Anmeldung erfolgreich. Dann fehlen Rechte auf einer bestimmten Datenbank. Sehen Sie nach, was das Konto wirklich darf:
mariadb -e "SHOW GRANTS FOR 'app'@'localhost';"
FLUSH PRIVILEGES brauchen Sie nur, wenn Sie die Rechtetabellen direkt per INSERT oder UPDATE verändert haben. Nach GRANT, CREATE USER oder ALTER USER ist es überflüssig, und es verdeckt gelegentlich, dass der eigentliche Befehl gar nicht gegriffen hat.
Der Client versteht das Plugin nicht
MySQL 8.0 nutzt standardmäßig caching_sha2_password. Ältere Clients und Bibliotheken quittieren das mit ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded. Das ist kein Rechteproblem, sondern eine Inkompatibilität. In MySQL 8.0 steht mysql_native_password als Ausweichlösung noch zur Verfügung, in MySQL 8.4 ist es entfernt. Aktualisieren Sie im Zweifel lieber den Client, statt die Verschlüsselung zurückzudrehen.
Root-Passwort zurücksetzen
Wenn nichts mehr hilft, muss der Server einmal ohne Rechteprüfung starten. Zwei Wege führen zum Ziel. Der Weg über --init-file ist der sicherere, weil der Server dabei durchgehend mit aktiver Rechteprüfung läuft.
Variante 1: init-file (empfohlen)
Die SQL-Datei muss an einem Ort liegen, den der Dienst lesen darf. Unter /tmp oder /root scheitert das auf Ubuntu regelmäßig an AppArmor und an PrivateTmp im systemd-Dienst. Legen Sie sie deshalb in das Datenverzeichnis:
sudo systemctl stop mariadb
sudo tee /var/lib/mysql/kh-reset.sql >/dev/null <<'SQL'
ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket
OR mysql_native_password USING PASSWORD('NeuesRootPasswort');
SQL
sudo chown mysql:mysql /var/lib/mysql/kh-reset.sql
sudo systemctl set-environment MYSQLD_OPTS="--init-file=/var/lib/mysql/kh-reset.sql"
sudo systemctl start mariadb
Danach unbedingt aufräumen, sonst läuft der Server bei jedem Start erneut mit der Datei:
sudo systemctl unset-environment MYSQLD_OPTS
sudo rm /var/lib/mysql/kh-reset.sql
sudo systemctl restart mariadb
Für MySQL 8.0 auf Ubuntu lautet der Dienst mysql und die SQL-Zeile:
ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'NeuesRootPasswort';
Variante 2: skip-grant-tables
Diese Variante schaltet die Rechteprüfung komplett ab. Ohne --skip-networking hätte in dieser Zeit jeder, der den Port erreicht, vollen Zugriff auf alle Daten. Die Option ist deshalb nicht optional, sondern Pflicht.
sudo systemctl stop mariadb
sudo systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"
sudo systemctl start mariadb
sudo mariadb
In der Sitzung zuerst die Rechtetabellen laden, sonst lehnt der Server ALTER USER mit einer Fehlermeldung ab:
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket
OR mysql_native_password USING PASSWORD('NeuesRootPasswort');
EXIT;
Und dann wieder in den Normalbetrieb:
sudo systemctl unset-environment MYSQLD_OPTS
sudo systemctl restart mariadb
Falls Ihre Distribution die Variable MYSQLD_OPTS nicht auswertet, funktioniert immer die Drop-in-Datei unter /etc/systemd/system/mariadb.service.d/reset.conf mit dem Inhalt [Service] und Environment="MYSQLD_OPTS=--skip-grant-tables --skip-networking", gefolgt von sudo systemctl daemon-reload. Löschen Sie die Datei danach wieder und laden Sie systemd erneut.
Wenn der Reset schiefgeht
Genau hier hören die meisten Anleitungen auf. Die vier häufigsten Folgeprobleme:
Der Dienst startet nicht mehr. Meist ein Tippfehler in der SQL-Datei. Der Server bricht dann beim Start ab. Debian protokolliert MariaDB standardmäßig in das Journal, Ubuntu mit MySQL zusätzlich in eine Datei:
sudo journalctl -u mariadb -n 50 --no-pager
sudo tail -n 50 /var/log/mysql/error.log
Wo der Server hinschreibt, lässt sich mit SHOW VARIABLES LIKE 'log_error'; nachsehen. Erwarten Sie dort aber keinen Dateipfad: auf den Paketinstallationen von Debian und Ubuntu ist der Wert leer, weil der Dienst mit --skip-log-error läuft. Alles geht dann an die Standardfehlerausgabe und landet im Journal beziehungsweise in /var/log/syslog, abzurufen mit journalctl -u mariadb. Zur Behebung entfernen Sie die --init-file-Umgebungsvariable und starten neu.
Der Server läuft dauerhaft ohne Rechteprüfung. Das passiert, wenn das unset-environment vergessen wurde, und es ist die gefährlichste Variante, weil nach außen alles normal aussieht. Zwei Kontrollen:
systemctl show-environment
ps -o args= -C mariadbd
Taucht in einer der beiden Ausgaben skip-grant-tables auf, ist die Rechteprüfung noch aus. systemctl show-environment setzt dabei voraus, dass systemd als PID 1 läuft. Auf einem gewöhnlichen Server ist das gegeben, in einem Container oder auf einem System mit SysV-Init gibt es den Befehl nicht. Dort lesen Sie die Umgebung direkt aus dem laufenden Prozess:
cat /proc/$(pgrep -n mariadbd)/environ | tr '\0' '\n'
Auf die Argumentliste allein sollten Sie sich ebenfalls nicht verlassen: über systemd gestartet zeigt ps häufig nur /usr/sbin/mariadbd ohne jede Option, während über ein SysV-Init-Skript die vollständige Liste erscheint. Ein weiteres Indiz: unter skip-grant-tables antwortet SHOW GRANTS mit einer Fehlermeldung statt mit Rechten.
AppArmor blockiert die init-Datei. Symptom ist ein Server, der ohne erkennbaren Grund nicht hochkommt. Ein Blick in das Kernel-Log klärt das:
sudo dmesg | grep -i denied
Sie sperren sich vollständig aus. Das geschieht, wenn man root per ALTER USER ... IDENTIFIED BY auf ein reines Passwort umstellt, dabei die Socket-Authentifizierung verliert und das neue Passwort danach nicht mehr weiß. Der Ausweg ist derselbe Reset erneut, diesmal mit der oben gezeigten Doppelvariante unix_socket OR mysql_native_password. Legen Sie sich vor jedem Eingriff eine Kopie der Rechtetabellen an, das dauert Sekunden und erspart im Ernstfall Stunden:
sudo mariadb-dump mysql > /root/mysql-grants.sql
Die sonst übliche Option --single-transaction können Sie sich hier sparen. Sie läuft zwar ohne Fehler durch, wirkt aber nicht, weil die Tabellen der Datenbank mysql auf Aria beziehungsweise MyISAM liegen und damit nicht transaktional sind.
Auf gemanagtem Webhosting haben Sie keinen Systemzugriff und damit keine dieser Optionen. Dort werden Datenbankbenutzer und Passwörter über die Verwaltungsoberfläche zurückgesetzt. Auf einem KVM-Rootserver oder Dedicated Server bei KernelHost haben Sie vollen Root-Zugriff, und falls der Server über die Netzwerkverbindung nicht mehr erreichbar ist, kommen Sie über die Konsole im Kundenbereich an das System.
Prüfen, ob es wirklich behoben ist
Dass ein Befehl ohne Fehler durchläuft, heißt noch nicht, dass die Anmeldung dauerhaft funktioniert. Diese vier Kontrollen decken die typischen Restfehler auf.
Erstens, der Unterschied zwischen USER() und CURRENT_USER(). Die erste Funktion zeigt, als was Sie sich ausgegeben haben, die zweite, welche Kontozeile der Server tatsächlich benutzt:
mariadb -u app -p'Passwort' -e "SELECT USER(), CURRENT_USER();"
Stehen dort zwei verschiedene Werte, etwa app@localhost und app@%, dann greifen Ihre Rechte über eine andere Zeile als gedacht. Das erklärt später auftretende 1044-Fehler, bevor sie im Produktivbetrieb auffallen.
Zweitens, ein echter Zugriff auf die Zieldatenbank statt nur einer Anmeldung, und die Auswertung des Rückgabewerts:
mariadb -u app -p'Passwort' meine_db -e "SELECT 1;" ; echo "Rueckgabewert: $?"
Drittens, ein Neustart des Dienstes und danach dieselbe Anmeldung erneut. Damit stellen Sie sicher, dass die Änderung wirklich in den Tabellen steht und nicht nur im laufenden Speicher, und dass keine Umgebungsvariable aus dem Reset übriggeblieben ist:
sudo systemctl restart mariadb
sudo systemctl is-active mariadb
Viertens, die Anwendung selbst. Ein erfolgreicher Test auf der Kommandozeile sagt wenig über eine PHP-Anwendung aus, die sich unter dem Benutzer des Webservers und über den Socket verbindet. Testen Sie deshalb unter demselben Systembenutzer:
sudo -u www-data mariadb -u app -p'Passwort' meine_db -e "SELECT CURRENT_USER();"
Unterschiede zwischen den Distributionen
Eine Anleitung, die für alle vier Systeme dasselbe behauptet, ist an mindestens einer Stelle falsch. Diese Tabelle fasst die relevanten Abweichungen zusammen:
| System | Standard-Datenbank | Socket-Plugin | Hinweis |
|---|---|---|---|
| Debian 13 | MariaDB 11.8 | unix_socket | kein Paket mysql-server verfügbar; mysql ist nur noch ein Symlink auf mariadb und warnt beim Aufruf |
| Debian 12 | MariaDB 10.11 | unix_socket | kein Paket mysql-server verfügbar |
| Ubuntu 24.04 | MariaDB 10.11 oder MySQL 8.0 | unix_socket bzw. auth_socket | beide Pakete parallel in den Quellen, Verwechslungsgefahr bei Anleitungen |
| Ubuntu 22.04 | MariaDB 10.6 oder MySQL 8.0 | unix_socket bzw. auth_socket | JSON_VALUE und JSON_QUERY auf mysql.global_priv arbeiten hier genauso wie unter 10.11 und 11.8; auch in 10.6 ist mysql.user nur eine Sicht, für die vollständige Auth-Kette bleibt SHOW CREATE USER der kürzeste Weg |
Weitere Unterschiede, die in der Praxis Zeit kosten: Die Konfigurationsdateien liegen bei MariaDB unter /etc/mysql/mariadb.conf.d/50-server.cnf, bei MySQL unter /etc/mysql/mysql.conf.d/mysqld.cnf. Die Dienstnamen lauten mariadb beziehungsweise mysql, wobei MariaDB zusätzlich einen Alias mysql mitbringt. Bei MariaDB gibt es seit Version 10.4 keinen Benutzer debian-sys-maint mehr, die Datei /etc/mysql/debian.cnf verweist dort auf root über den Socket. Bei MySQL auf Ubuntu existiert der Wartungsbenutzer weiterhin, und das ist der beste Notzugang, wenn das Root-Passwort verloren ist und Sie den Server nicht neu starten wollen:
sudo mysql --defaults-file=/etc/mysql/debian.cnf
Wenn Sie diese Reihenfolge einhalten, also erst die Meldung genau lesen, dann Plugin und Hostzeile prüfen, dann den Verbindungsweg, und erst ganz zuletzt das Passwort zurücksetzen, lösen sich die allermeisten Fälle in wenigen Minuten und ohne Ausfallzeit. Der Reset über skip-grant-tables ist das letzte Mittel, nicht der erste Schritt.
Häufige Fragen
Warum funktioniert mysql -u root -p nicht, obwohl das Passwort stimmt?
Was ist der Unterschied zwischen ERROR 1045 und ERROR 1698?
Ist localhost dasselbe wie 127.0.0.1?
Ist die Großschreibung bei Benutzername und Host relevant?
Wie setze ich das Root-Passwort ohne skip-grant-tables zurück?
Woran erkenne ich, dass der Server noch ohne Rechteprüfung läuft?
Gibt es unter Debian ein Paket mysql-server?
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.

