Fix: Access denied for user 'root'@'localhost' (MySQL ERROR 1045)

The Error

You try to connect to MySQL and get:

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

Or one of these variations:

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
Access denied for user 'myuser'@'localhost' (using password: YES)
Access denied for user 'root'@'127.0.0.1' (using password: YES)
SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES)

All of these mean the same thing: MySQL rejected your login credentials. The server is running and reachable, but it won’t let you in.

Why This Happens

MySQL authenticates users based on three things: username, host, and authentication method (password or plugin). A mismatch in any of these causes ERROR 1045.

Common causes:

  • Wrong password. The password you’re providing doesn’t match what MySQL has stored for that user.
  • The root account uses auth_socket or unix_socket instead of a password. On Ubuntu/Debian, MySQL and MariaDB often configure root to authenticate via the OS user, not a password. Running mysql -u root -p fails because root expects socket auth, not a password.
  • caching_sha2_password plugin incompatibility. MySQL 8.0+ defaults to caching_sha2_password, which older clients and libraries don’t support.
  • Host mismatch. MySQL treats 'root'@'localhost', 'root'@'127.0.0.1', and 'root'@'%' as different accounts. You may have set the password for one but are connecting as another.
  • The user doesn’t exist. The user account was never created, or was dropped.
  • Docker MySQL root password not set or not matching. The MYSQL_ROOT_PASSWORD environment variable doesn’t match what you’re using to connect.
  • Application connection string has wrong credentials. The password in your .env file, config, or connection string is stale or incorrect.

Fix 1: Use the Correct Password

Start with the obvious. Make sure you’re using the right password.

If you set a root password during installation:

mysql -u root -p

MySQL will prompt for the password. Type it carefully — the terminal won’t show any characters.

If you’re passing the password on the command line (not recommended for production):

mysql -u root -p'yourpassword'

Note: no space between -p and the password.

“using password: NO” means you didn’t provide a password at all. Either add -p to prompt for one, or check your connection string.

“using password: YES” means you provided a password, but it’s wrong.

Fix 2: Fix the Auth Plugin (auth_socket / unix_socket)

On Ubuntu, Debian, and many Linux distributions, MySQL and MariaDB configure the root account to use auth_socket (MySQL) or unix_socket (MariaDB) authentication by default. This means root can only log in as the OS root user — no password required, but only via sudo.

This works:

sudo mysql

This fails with ERROR 1045:

mysql -u root -p

Check the current auth plugin

sudo mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"

You’ll see something like:

+------+-----------+-----------------------+
| user | host      | plugin                |
+------+-----------+-----------------------+
| root | localhost | auth_socket           |
+------+-----------+-----------------------+

Switch root to password authentication

sudo mysql

For MySQL 8.0+:

ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'your_new_password';
FLUSH PRIVILEGES;

For MySQL 5.7 or if your clients don’t support caching_sha2_password:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_new_password';
FLUSH PRIVILEGES;

For MariaDB:

ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('your_new_password');
FLUSH PRIVILEGES;

Now mysql -u root -p works with your new password.

Alternative approach: Instead of switching root to password auth, create a separate admin user and keep root on socket auth. This is actually more secure:

CREATE USER 'admin'@'localhost' IDENTIFIED BY 'strong_password';
GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;

Fix 3: Reset the Root Password (skip-grant-tables)

If you’ve lost the root password entirely, reset it using safe mode.

Step 1: Stop MySQL

Linux (systemd):

sudo systemctl stop mysql
# or for some distributions:
sudo systemctl stop mysqld

macOS (Homebrew):

brew services stop mysql

Windows:

net stop MySQL80

Step 2: Start MySQL with —skip-grant-tables

This starts MySQL without any authentication checks.

Linux:

sudo mysqld_safe --skip-grant-tables --skip-networking &

The --skip-networking flag prevents remote connections while the server is running without authentication. This is critical for security.

macOS:

sudo mysqld_safe --skip-grant-tables --skip-networking &

Windows: Open an elevated Command Prompt:

mysqld --skip-grant-tables --skip-networking

Step 3: Connect and reset the password

mysql -u root

For MySQL 8.0+:

FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'your_new_password';

For MySQL 5.7:

FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'your_new_password';

For older MySQL versions (5.6 and below):

FLUSH PRIVILEGES;
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('your_new_password');

FLUSH PRIVILEGES must come first when running with --skip-grant-tables, or the ALTER USER command fails.

Step 4: Restart MySQL normally

Kill the safe mode process and restart:

sudo killall mysqld
sudo systemctl start mysql

Test the new password:

mysql -u root -p

Fix 4: Fix caching_sha2_password Issues (MySQL 8.0+)

MySQL 8.0 changed the default authentication plugin from mysql_native_password to caching_sha2_password. Many older clients, libraries, and ORMs don’t support this plugin and fail with ERROR 1045 or a related authentication error.

Symptoms

Your client shows one of these:

Authentication plugin 'caching_sha2_password' cannot be loaded
ERROR 1045 (28000): Access denied for user 'root'@'localhost'

Option A: Switch the user to mysql_native_password

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';
FLUSH PRIVILEGES;

Option B: Change the server default for all new users

Edit my.cnf (or my.ini on Windows):

[mysqld]
default_authentication_plugin=mysql_native_password

On MySQL 8.4+, this setting was replaced:

[mysqld]
authentication_policy=mysql_native_password

Restart MySQL after editing.

Option C: Update your client

The better long-term solution is to update your MySQL client library to one that supports caching_sha2_password:

  • PHP: Use mysqlnd (PHP 7.4+) or upgrade to PHP 8.x.
  • Python: Use mysql-connector-python 8.0+ or PyMySQL 1.0+.
  • Node.js: Use mysql2 instead of mysql.
  • Java: Use MySQL Connector/J 8.0+.

Fix 5: Fix Host Mismatches

MySQL treats these as completely different user accounts:

  • 'root'@'localhost' — connects via Unix socket or TCP to localhost
  • 'root'@'127.0.0.1' — connects via TCP to 127.0.0.1
  • 'root'@'%' — connects from any host

You may have set a password for 'root'@'%' but you’re connecting to localhost, which uses the 'root'@'localhost' account (with a different — or no — password).

Check which user accounts exist

SELECT user, host, plugin FROM mysql.user;

Create the missing account or fix the password

If 'root'@'127.0.0.1' is missing:

CREATE USER 'root'@'127.0.0.1' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION;
FLUSH PRIVILEGES;

If the password is wrong on one of them:

ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY 'your_password';
FLUSH PRIVILEGES;

How to tell which host you’re connecting from

  • mysql -u root -p (no -h flag) uses the Unix socket on Linux/macOS, which matches 'root'@'localhost'.
  • mysql -u root -p -h 127.0.0.1 uses TCP, which matches 'root'@'127.0.0.1'.
  • mysql -u root -p -h localhost uses the Unix socket on Linux, TCP on Windows.

To force TCP even with localhost:

mysql -u root -p -h 127.0.0.1 --protocol=TCP

Fix 6: Fix Docker MySQL Root Password

Docker MySQL containers set the root password via environment variables at first startup only. Changing the environment variable after the data volume already exists has no effect.

Check your docker-compose.yml or docker run command

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: your_password
    ports:
      - "3306:3306"

Or:

docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=your_password -p 3306:3306 mysql:8.0

Common Docker mistakes

Connecting from the host to the container:

mysql -u root -p -h 127.0.0.1 -P 3306

Use 127.0.0.1, not localhost. On Linux, localhost tries the Unix socket, which doesn’t connect to the container.

Connecting from another container:

Use the service name as the hostname, not localhost (see ERR_CONNECTION_REFUSED on localhost for why localhost doesn’t work across containers):

mysql://root:your_password@db:3306/mydb

Password was changed but old volume persists:

If you change MYSQL_ROOT_PASSWORD and the container still rejects your login, the password was set during the first initialization and is stored in the data volume.

Fix it by removing the volume and reinitializing:

docker compose down -v
docker compose up -d

Warning: This deletes all data in the MySQL volume. Export your data first if needed.

Or reset the password inside the running container:

docker exec -it mysql mysql -u root -p -e "ALTER USER 'root'@'%' IDENTIFIED BY 'new_password'; FLUSH PRIVILEGES;"

MYSQL_ALLOW_EMPTY_PASSWORD vs MYSQL_ROOT_PASSWORD:

If you set MYSQL_ALLOW_EMPTY_PASSWORD=yes, root has no password. Connect with:

mysql -u root -h 127.0.0.1

Not with -p.

Fix 7: Fix Application Connection Strings

Your code connects fine from the command line but your app gets ERROR 1045. The problem is usually in the connection string or environment variable.

Check your connection string format

mysql://user:password@host:port/database

Common issues:

  • Special characters in the password need to be URL-encoded. p@ss#word becomes p%40ss%23word.
  • The port is wrong. Default is 3306, but Docker may map it to a different port.
  • The host is wrong. Inside Docker, use the container/service name. From the host, use 127.0.0.1.

Check your environment variables

Make sure your app is actually reading the right config. A common mistake is having the password in .env but the app reading from a different config file, or the environment variable not being loaded.

# Print the DATABASE_URL (mask the password in production)
echo $DATABASE_URL

PHP (PDO) specific

// Wrong -- password has special characters that break parsing
$pdo = new PDO('mysql:host=localhost;dbname=mydb', 'root', 'p@ss');

// Right -- use the options array
$pdo = new PDO('mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4', 'root', 'p@ss');

Note: PHP’s PDO with localhost uses the Unix socket. Use 127.0.0.1 to force TCP, especially in Docker setups.

Still Not Working?

The anonymous user is shadowing your account

MySQL may have an anonymous user (''@'localhost') that takes priority over your named account. This is common on older MySQL installations.

Check for anonymous users:

SELECT user, host FROM mysql.user WHERE user = '';

If any rows return, delete them:

DROP USER ''@'localhost';
DROP USER ''@'%';
FLUSH PRIVILEGES;

Run mysql_secure_installation to clean up other default security issues.

MySQL is reading a different config file

MySQL reads options files from multiple locations. A stale password in ~/.my.cnf or /etc/mysql/conf.d/ can override what you’re typing.

Check which config files MySQL reads:

mysql --help | grep -A 1 "Default options"

Look for a [client] section with a hardcoded password:

[client]
user=root
password=old_wrong_password

Remove or update it.

The user exists for a different host only

You have 'myuser'@'%' but the connection resolves to localhost, and MySQL prefers more specific host matches. A 'myuser'@'%' account does not match connections from localhost if there’s any other entry for localhost (even the anonymous user).

Fix it by creating the localhost-specific account:

CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost';
FLUSH PRIVILEGES;

MySQL data directory has wrong permissions

After a manual installation, upgrade, or restore, the MySQL data directory may be owned by the wrong user. This is a common file permission issue on Linux:

ls -la /var/lib/mysql

It should be owned by mysql:mysql. Fix it:

sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl restart mysql

SELinux is blocking MySQL (RHEL/CentOS/Fedora)

SELinux may prevent MySQL from reading its data files or socket:

sudo ausearch -m avc -ts recent | grep mysql

If you see denials:

sudo setsebool -P mysql_connect_any 1
sudo restorecon -rv /var/lib/mysql

Temporary password expired (fresh MySQL 8.0 install)

On fresh MySQL 8.0 installations (especially on RHEL-based systems), MySQL generates a temporary root password. You must change it before doing anything else.

Find the temporary password:

sudo grep 'temporary password' /var/log/mysqld.log

Log in with it:

mysql -u root -p

MySQL forces you to change it immediately:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourNewStrongPassword!1';

The new password must meet MySQL’s validate_password policy (uppercase, lowercase, digit, special character, at least 8 characters). To lower the policy for development:

SET GLOBAL validate_password.policy = LOW;
SET GLOBAL validate_password.length = 6;

Related: If you’re troubleshooting database connectivity beyond authentication, see Fix: PostgreSQL Connection Refused. For Docker networking and file issues, see Fix: Docker COPY Failed: File Not Found and Fix: Docker Permission Denied. If your app can’t read the database credentials from environment variables, see Fix: Environment Variable Is Undefined.

Related Articles