Skip to content

Laravel error

Laravel: SQLSTATE[HY000] [1045] Access denied for user

The error

SQLSTATE[HY000] [1045] Access denied for user

MySQL rejected the credentials. The password is usually correct - what is wrong is which host the user is allowed to connect from, or which .env the application actually read.

The error

Illuminate\Database\QueryException

SQLSTATE[HY000] [1045] Access denied for user 'app'@'10.0.1.14' (using password: YES)

Read the whole line before changing anything. It contains three facts, and one of them is usually the answer: the user name, the host MySQL saw the connection arrive from, and whether a password was sent at all.

What it means

MySQL refused the credentials for that user from that host. A MySQL account is not a user name - it is a name and a host together. 'app'@'localhost' and 'app'@'%' are two separate accounts that can have two separate passwords, and granting one does nothing for the other.

First: did the application read the file you edited?

This is the most common cause and it wastes the most time, because everything on the database side is correct.

php artisan config:clear
php artisan queue:restart

A cached config file holds the values from whenever it was built. Editing .env after that changes nothing until the cache is rebuilt. Queue workers and Octane processes additionally hold their configuration in memory for their whole life, so they keep using the old credentials until they are restarted - which is why this often presents as "the website works, the jobs fail".

Check what the application actually has:

php artisan tinker --execute="dd(config('database.connections.mysql'));"

If that does not match your .env, the problem is on this side and no grant will fix it.

"using password: NO" means the value was empty

The application sent nothing. Almost always a .env parsing problem:

DB_PASSWORD=pa$$w0rd#2026     # truncated at the #
DB_PASSWORD="pa$$w0rd#2026"   # correct

Quote any value containing #, a space, or a leading or trailing character you care about. A key that is missing entirely also reads as an empty string rather than raising anything.

Then: the host in the message

Take the host from the error and check what exists for it:

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

If the error says 'app'@'10.0.1.14' and the table only has 'app'@'localhost', that account does not exist as far as MySQL is concerned:

CREATE USER 'app'@'10.0.1.14' IDENTIFIED BY 'secret';
GRANT ALL PRIVILEGES ON appdb.* TO 'app'@'10.0.1.14';
FLUSH PRIVILEGES;

Grant to the narrowest host that works. 'app'@'%' is convenient and means the account may connect from anywhere the network permits.

There is a variant of this on a single machine: DB_HOST=127.0.0.1 connects over TCP and matches a host entry, while DB_HOST=localhost uses the unix socket and matches 'app'@'localhost'. The same string in two configurations can therefore be two different accounts.

In Docker

environment:
  MYSQL_PASSWORD: secret

These variables are read only when the data directory is empty. Changing one on an existing volume changes what your application sends and leaves the stored account untouched. Either set the password inside the running database, or remove the volume and let it initialise again - which deletes the data, so not on anything you need.

Also worth checking: the application must use the service name as its host, not localhost. Inside a container localhost is that container.

Confirm outside the application

mysql -h 127.0.0.1 -u app -p appdb

Succeeding here while Laravel fails puts the fault firmly in configuration - the cached config, an unrestarted worker, or a different .env than the one you edited.

If the connection never gets as far as being refused, and PDO simply says it cannot find a driver, the problem is the extension and not the password. On a fresh machine that error usually comes first.

The unrestarted-worker case generalises. A long-lived process keeps the environment it booted with, so a corrected password reaches the web requests and not the queue. And if the credentials are fine and the server is refusing further connections, the message changes.

Related questions

The password is definitely right. We just pasted it in.
Then check whether the application read the file you edited. A cached configuration keeps the previous values until it is rebuilt, and a queue worker or Octane process keeps them until it is restarted. Both look exactly like a wrong password.
It works from the command line but not from the application.
Usually a host mismatch. A MySQL user is a pair of a name and a host, so an account granted for localhost is a different account from the same name granted for a remote address - and a connection over TCP is not a connection over the local socket, even on the same machine.
It says "using password: NO".
The application sent no password at all, which means the value it read was empty. An unquoted .env value containing a # is truncated at that character, and a missing key reads as an empty string rather than an error.
It started happening in Docker without any change.
The MySQL official image applies its environment variables only when it initialises an empty data directory. If the volume already exists, changing MYSQL_PASSWORD in the compose file changes what the application sends and not what the database expects.
Call us+1 848 272 7583WhatsApp+90 850 308 5436Emailinfo@codefacture.comContact page