Common causes
- Wrong or stale password in the app config (.env, wp-config.php, config.php), often after a host migration or password rotation
- The account exists for a different host: 'app'@'localhost' does not match a connection arriving as 'app'@'127.0.0.1' or from another server
- root on Debian/Ubuntu uses unix_socket or auth_socket, so password logins fail with ERROR 1698 unless you run sudo mysql
- "using password: NO" means the client sent no password at all: an empty DB_PASSWORD or an unquoted special character in the config
- The user can log in but has no privileges on that database (ERROR 1044)
- An old client or PHP driver cannot handle the server's authentication plugin, such as caching_sha2_password on MySQL 8
How to fix it
- Test the exact credentials. Run mysql -u app -p -h 127.0.0.1 shop and paste the password from your config file. If this works, the app is reading different values; if it fails, the problem is on the server side.
- Check the host part of the account. As an admin, run SELECT user, host, plugin FROM mysql.user WHERE user='app'; Compare the host column with how the app connects: 'localhost' means the Unix socket, 127.0.0.1 or a remote IP needs a matching host or '%'.
- Reset the password. Run ALTER USER 'app'@'localhost' IDENTIFIED BY 'NewStrongPass'; then update the app config. Quote the value in .env if it contains #, spaces or $.
- Grant privileges on the database. Run GRANT ALL PRIVILEGES ON shop.* TO 'app'@'localhost'; for ERROR 1044. In MySQL 8 GRANT no longer creates users, so CREATE USER first if the account is missing.
- Use sudo for socket-authenticated root. On Ubuntu and Debian, sudo mysql logs in as root through the socket. Create a separate application user instead of switching root to password authentication.
- Recover a lost root password. Stop the server, start it with --skip-grant-tables --skip-networking, run FLUSH PRIVILEGES; then ALTER USER 'root'@'localhost' IDENTIFIED BY '...'; and restart normally. Never leave skip-grant-tables enabled.
MySQL 8 / MariaDB SQL
CREATE USER 'app'@'localhost' IDENTIFIED BY 'NewStrongPass';
GRANT ALL PRIVILEGES ON shop.* TO 'app'@'localhost';
-- if the app connects over TCP from the same box:
CREATE USER 'app'@'127.0.0.1' IDENTIFIED BY 'NewStrongPass';
GRANT ALL PRIVILEGES ON shop.* TO 'app'@'127.0.0.1'; How to stop it happening again
- Give each application its own user limited to its own database instead of sharing root
- Store credentials in one config file or secret store and update it whenever passwords rotate
- Decide on localhost (socket) or 127.0.0.1 (TCP) and create the account for that host
- Include database user creation and grants in your migration or deployment checklist