ERROR 1045 (28000): Access denied for user 'app'@'localhost' (using password: YES)
MySQL found no account that matches this user, from this host, with this password. The error message itself contains most of the clues: the user name, the host MySQL thinks you’re connecting from, and whether a password was sent.
Read the message
'app'@'localhost'— the account MySQL tried to match.localhostmeans you connected through the Unix socket (or vialocalhostname).'app'@'10.0.0.6'— a TCP connection from that IP.(using password: NO)— no password was sent at all (missing-p, empty config value).(using password: YES)— a password was sent but didn’t match any account for that user@host.
Cause 1: root on Ubuntu/Debian uses socket authentication
mysql -u root -p # fails with 1045
sudo mysql # works
On Ubuntu’s MySQL and Debian’s MariaDB, root@localhost authenticates by Linux user (auth_socket / unix_socket), not by password. Use sudo mysql, and create a separate password-based user for tools and applications. See resetting the MySQL root password.
Cause 2: wrong password
Simple, but check carefully:
- Special characters in the password breaking the config file or shell (
$,!, quotes). Quote the value; in.envfiles wrap it in quotes. - The application reading a different config than you think (cached config in Laravel:
php artisan config:clear).
Reset it as an administrator:
ALTER USER 'app'@'localhost' IDENTIFIED BY 'new-long-password';
Cause 3: user@host doesn’t match
MySQL accounts are user and host. Check what exists:
SELECT user, host, plugin FROM mysql.user WHERE user = 'app';
Typical mismatches:
- The account is
'app'@'localhost'but the app connects over TCP to127.0.0.1→ MySQL may look for'app'@'127.0.0.1'. Create it, or connect via the socket (localhost). - The account is
'app'@'localhost'but the app runs on another server or in a Docker container (connections come from172.17.0.x). Create the account for that host or subnet ('app'@'172.17.%'), and see MySQL remote access. - Behind NAT, MySQL sees the NAT’s public IP, not your laptop’s.
Create the missing account:
CREATE USER 'app'@'127.0.0.1' IDENTIFIED BY 'the-password';
GRANT ALL PRIVILEGES ON shop.* TO 'app'@'127.0.0.1';
Cause 4: anonymous users shadow your account
Old installations may contain anonymous accounts (''@'localhost'). MySQL matches the most specific host first, so ''@'localhost' can win over 'app'@'%' — and then your password doesn’t match. Remove them:
SELECT user, host FROM mysql.user WHERE user = '';
DROP USER ''@'localhost';
(mysql_secure_installation does this.)
Cause 5: authentication plugin problems
MySQL 8 defaults to caching_sha2_password. Very old clients or drivers fail — sometimes reported as access denied, sometimes as “authentication method unknown”. Update the client/driver first; as a fallback for a single account:
ALTER USER 'app'@'localhost' IDENTIFIED WITH mysql_native_password BY 'the-password';
(mysql_native_password is deprecated and disabled by default in MySQL 8.4+.)
Cause 6: the user lacks privileges on the database
That’s usually a different error (1044, “Access denied … to database”), but check:
SHOW GRANTS FOR 'app'@'localhost';
A systematic check
# 1. Socket connection
mysql -u app -p shop
# 2. TCP connection
mysql -u app -p -h 127.0.0.1 shop
Inside a successful session, SELECT USER(), CURRENT_USER(); shows the identity you presented vs the account MySQL matched — revealing mismatches immediately.
Locked out completely?
If no administrative login works, reset access with the server started in --skip-grant-tables mode — see reset MySQL root password.
Related: MySQL users and privileges and error 2002 (socket).