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. localhost means you connected through the Unix socket (or via localhost name).
  • '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 .env files 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 to 127.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 from 172.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).

  • MySQL
  • MariaDB
  • Troubleshooting