MySQL ERROR 1045 (28000): Access denied for user — find which part is wrong
Fix MySQL "ERROR 1045 Access denied for user": check the user@host account, the password, socket vs TCP, option files, and Docker init passwords.
by OhSudo ·
You try to connect and MySQL says no:
$ mysql -u app -p shopEnter password:ERROR 1045 (28000): Access denied for user 'app'@'localhost' (using password: YES)The message looks like “wrong password”, and often it is. But MySQL gives the same message for every login failure, on purpose, so it doesn’t help attackers. Read the three parts of it carefully — each one is a clue:
'app': the user name the server received.@'localhost': the host the server thinks you are connecting from. In MySQL, an account is a user and a host:'app'@'localhost'and'app'@'%'are two different accounts, with different passwords.(using password: YES): a password was sent.NOmeans none was sent at all.
TL;DR
mysql -u app -p -h 127.0.0.1 # -p alone: type the password at the promptsudo mysql # admin login on Ubuntu/Debian (socket auth)-- as an admin: which accounts exist for this user, and how do they log in?SELECT user, host, plugin FROM mysql.user WHERE user = 'app';
-- set a new password for the exact accountALTER USER 'app'@'%' IDENTIFIED BY 'new-password-here';1. “using password: NO” — no password was sent
The client didn’t send a password, so the server refused the login. Usual causes:
- You forgot
-p.mysql -u app shopsends no password. - The app’s config has an empty or missing password variable (check the environment the app really runs with, not your shell).
Use -p without the password after it, and type it at the prompt. Passwords on the command line end up in your shell history and in the process list.
2. Wrong password (or the shell changed it)
If you are sure the password is right, check that it reaches MySQL unchanged. In a shell, $, !, & and spaces have special meanings:
mysql -u app -pS3cret$2024 shop # the shell replaces $2 -> the password is wrongmysql -u app -p'S3cret$2024' shop # single quotes: sent exactly as writtenmysql -u app -p shop # best: type it at the promptThe same applies to passwords in .env files, YAML and connection URLs — in a URL, characters like @, : and / must be URL-encoded (@ → %40).
Also check that no option file sends a different user or password without you noticing:
mysql --print-defaultsThis prints the options the client reads from ~/.my.cnf, /etc/mysql/… and similar files. Options there apply to every mysql command you run.
3. The right user, but the wrong host
The account is user@host, and MySQL picks the most specific matching host. Check what exists:
SELECT user, host, plugin FROM mysql.user WHERE user = 'app';+------+-----------+-----------------------+| user | host | plugin |+------+-----------+-----------------------+| app | 10.0.0.% | caching_sha2_password |+------+-----------+-----------------------+Here app may only connect from 10.0.0.x. From the server itself you are localhost, and no 'app'@'localhost' account exists → 1045. Typical cases:
- The account was created for
localhost, and the app connects from another machine or container (or the other way around). - There are two accounts, for example
'app'@'localhost'and'app'@'%', with different passwords. From the server itself,localhostis more specific, so it wins. localhostand127.0.0.1are not the same thing:-h localhostconnects through the Unix socket,-h 127.0.0.1through TCP. Depending on the server’s settings, they can match different accounts.
Once you are logged in, MySQL tells you which account it matched:
SELECT USER(), CURRENT_USER();USER() is who you said you are; CURRENT_USER() is the account MySQL chose.
Create the account for the host you really connect from, and grant it what it needs. Use '%' only if the network around the server is protected (a firewall, a private network):
CREATE USER 'app'@'10.0.1.%' IDENTIFIED BY 'new-password-here';GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'app'@'10.0.1.%';The SQL Dialect Reference shows the same operation in MySQL, MariaDB, PostgreSQL, SQL Server and Oracle.
4. Docker: the password in the environment changed later
A classic with the official mysql and mariadb images:
services: db: image: mysql:8.4 environment: MYSQL_ROOT_PASSWORD: new-password # changed from old-password MYSQL_USER: app MYSQL_PASSWORD: new-app-password # changed too volumes: - db-data:/var/lib/mysqlThese variables are used only the first time, when the data directory is empty. After that, the users and passwords live in the volume, and changing the variables does nothing. The database still has the old passwords — and your app now uses the new ones.
Two ways out:
- Log in with the old password and change it with
ALTER USER(section 6). - If the data doesn’t matter (a fresh dev setup), remove the volume so the image initializes again:
docker compose down -v. This deletes all the data in it.
5. root on Ubuntu/Debian: use sudo, not a password
On Ubuntu and Debian, MySQL’s root usually logs in with the auth_socket plugin (unix_socket on MariaDB): no password, but you must be the Linux root user. So:
sudo mysqlworks, while mysql -u root -p as a normal user fails with ERROR 1698 (28000): Access denied for user ‘root’@‘localhost’ — a different code than 1045, which is your hint. Don’t change this to a password for the app’s sake: create a separate user for the application (section 3) and keep root for administration.
6. Reset a password
If you can log in as an admin (for example with sudo mysql), set a new password for the exact account from section 3:
ALTER USER 'app'@'%' IDENTIFIED BY 'new-password-here';No restart and no FLUSH PRIVILEGES are needed after ALTER USER.
Warning
Lost the root password too? The official procedure starts MySQL with --skip-grant-tables, which disables all permission checks — anyone who can connect gets full access. Add --skip-networking so only local connections are possible while you do it, and restart normally right after. Follow the steps in the MySQL manual (“How to Reset the Root Password”) for your version.
Shell, we have a problem!
Similar, but different: ERROR 1698 (28000) — the account uses socket authentication (section 5). ERROR 2059 or “Authentication plugin ‘caching_sha2_password’ cannot be loaded” — your client or driver is too old for MySQL 8’s default authentication: update it. ERROR 1524 “Plugin ‘mysql_native_password’ is not loaded” — MySQL 8.4 disables that old plugin by default; move the account to caching_sha2_password (ALTER USER … IDENTIFIED WITH caching_sha2_password BY '…'). ERROR 1044 Access denied for user … to database — the login worked, but the account has no rights on that database: that’s a GRANT problem.
Found a mistake? Let us know at contact@ohsudo.com.