Error 1045 means the server understood your login attempt and refused it. The fix is almost never the server itself — it is a mismatch between the user, the host part, the password or the grants the server has on record. This guide walks through each one in the order that finds the problem fastest, then shows how to recover when root is locked out.
Before you start
You need:
- SSH access to the VPS as a user who can run
sudo— see Connect to your Linux VPS with SSH if you have not done that yet. - The exact error text. Copy it whole; the part after
@and theusing passwordflag matter. - Root or sudo access to the database server, because the fixes happen inside MySQL.
Check which engine you are running, since the commands are nearly identical but the service names differ:
mysql --version
systemctl status mysql mariadb --no-pagerOn Ubuntu and Debian the service is usually mariadb or mysql. On AlmaLinux and Rocky it is mysqld.
Note: A full error line looks like ERROR 1045 (28000): Access denied for user 'deploy'@'localhost' (using password: YES). Write down the user, the host after @, and whether it says YES or NO.
Read the error line
Three pieces of that message tell you where to look:
- The user name — was it
deploy,root, or something your app config invented? - The host part —
localhost,127.0.0.1, or a remote IP. On Linux,localhostmeans a Unix socket, while127.0.0.1means TCP. These are different accounts to MySQL. using password: YESorNO—NOmeans the client sent no password at all, so the problem is configuration, not a wrong password.
If your app reports 1045 but the CLI works fine, the app is connecting to a different host string than you are. Check the app's config file before touching the database.
Check the user and host entry
Log in as the database administrator. On a fresh Ubuntu or Debian install, root uses socket authentication, so this works without a password:
sudo mysqlOn MariaDB the equivalent is sudo mariadb. Then list the accounts:
SELECT user, host, plugin FROM mysql.user WHERE user = 'deploy';What you want to see is an exact match for how the client connects. If your app uses 127.0.0.1, a row for 'deploy'@'localhost' will not help. 'deploy'@'%' matches any host, 'deploy'@'127.0.0.1' matches only TCP loopback.
Create the missing entry:
CREATE USER 'deploy'@'127.0.0.1' IDENTIFIED BY 'StrongPassw0rd!';Then set the password on the exact row you need, which is the cleanest way to rule out a stale credential:
ALTER USER 'deploy'@'127.0.0.1' IDENTIFIED BY 'StrongPassw0rd!';
FLUSH PRIVILEGES;If the plugin column shows unix_socket, that account only accepts logins from a matching Linux user over the socket — a password will never work. Change it with ALTER USER... IDENTIFIED VIA mysql_native_password USING PASSWORD('...') on MariaDB, or recreate the user on MySQL. Check your engine's documentation, because the authentication plugin defaults have changed between major versions.
Check the grants
A user can exist with a valid password and still be refused if it has no privileges on the target database. That normally returns error 1044, but misconfigured stacks often surface both. Inspect what the account actually holds:
SHOW GRANTS FOR 'deploy'@'127.0.0.1';If the output is only GRANT USAGE ON *.*, the account has no real rights. Grant what the application needs and no more:
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'deploy'@'127.0.0.1';
FLUSH PRIVILEGES;For a full setup walkthrough including remote access, see How to create a MySQL user and grant privileges and Install and secure MariaDB or MySQL.
Reset a lost root password
If you cannot get in at all — including as root — start the server with authentication disabled, change the password, and restart it normally.
Warning: This restarts the database and briefly allows passwordless local logins. Any application using the database will drop its connections. Do this in a quiet window, and keep --skip-networking so nothing can reach it from the network.
sudo systemctl stop mysql
sudo systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"
sudo systemctl start mysqlConnect and set a new password. Notice the FLUSH PRIVILEGES first — it is required, because with grant tables skipped the account rows are not loaded:
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStrongRootPassword!';On older MySQL 5.7-style servers use SET PASSWORD FOR 'root'@'localhost' = PASSWORD('NewStrongRootPassword!'); instead. Then stop the server, clear the override, and start it again:
sudo systemctl stop mysql
sudo systemctl unset-environment MYSQLD_OPTS
sudo systemctl start mysqlOn MariaDB, swap mysql for mariadb in every command above. On AlmaLinux and Rocky, the service unit is mysqld.
Check it works
Log in with the password rather than the socket shortcut, which proves the credential itself works:
mysql -u deploy -p -h 127.0.0.1 appdbEnter the password when asked. Then confirm you are in the right place and the account sees its data:
SELECT CURRENT_USER(), DATABASE();
SHOW TABLES;CURRENT_USER returns the account MySQL matched, including the host part — that is the row you were fighting with. Restart the application and recheck its logs; a cached connection pool may need a reload.
Troubleshooting
- Still 1045 after
ALTER USER→ you changed a different host row. RunSELECT user, host FROM mysql.user WHERE user = 'deploy';and change the row whose host matches the client's connection method exactly. - CLI works, application does not → the app config still holds the old password, or it connects to
localhost(socket) while you created127.0.0.1(TCP). Align both, and quote passwords containing#,;or spaces in.iniand.envfiles. using password: NO→ the client sent no password. Check for an empty password field, a missingMYSQL_PWD, or a--defaults-filethat points at the wrong config. See Where is my.cnf?.sudo mysqlworks butmysql -u root -pfails → that is socket authentication, not a bug. Set a password explicitly as shown above if you need password logins for root.- 1044 instead of 1045 → the login succeeded but the account lacks rights on that specific database. Grant privileges on the database named in the message.
