Laravel error
Laravel: SQLSTATE[HY000] [1045] Access denied for user
The error
SQLSTATE[HY000] [1045] Access denied for userMySQL 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:restartA 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" # correctQuote 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: secretThese 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 appdbSucceeding 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.
