How to Fix "FATAL: password authentication failed for user" in PostgreSQL
Quick answer
PostgreSQL rejects your connection attempt with a password authentication error, even when you're confident the password is correct. This error means...
PostgreSQL rejects your connection attempt with a password authentication error, even when you're confident the password is correct. This error means PostgreSQL received your connection request and actively checked your credentials against its authentication configuration β the fix depends on exactly which part of that check is failing.
The Problem
A normal connection attempt is rejected outright:
$ psql -U app_user -d mydb -h localhost
Password for user app_user:
psql: error: FATAL: password authentication failed for user "app_user"
Application connections fail with the equivalent driver-level error:
FATAL: password authentication failed for user "app_user"
SQLSTATE[08006]
Why It Happens
PostgreSQL's authentication is governed by pg_hba.conf, which defines exactly which authentication method applies for each combination of database, user, and connecting host β and a mismatch anywhere in that chain produces this same generic error regardless of the specific underlying cause. Common causes:
- The password is genuinely wrong, or was changed by another process without your knowledge.
- The role (user) doesn't actually exist, which PostgreSQL deliberately reports as the same generic authentication failure rather than a more specific "role not found" message, as a security measure against username enumeration.
pg_hba.confis configured with an authentication method that doesn't match how you're actually connecting β for example, requiringmd5orscram-sha-256for one connection type but the client is connecting in a way that maps to a different rule entirely.- A recent PostgreSQL upgrade changed the default password hashing method (from
md5toscram-sha-256starting in PostgreSQL 14), and an older client library doesn't support the newer method. - Environment variables or a connection string are silently overriding the password you think you're supplying interactively.
The Fix
First, confirm the role actually exists:
sudo -u postgres psql -c "\du"
List of roles
Role name | Attributes
-----------+------------------------------------------------------------
app_user |
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
If it's missing, create it:
sudo -u postgres psql -c "CREATE ROLE app_user WITH LOGIN PASSWORD 'your_password';"
If the role exists but you suspect the password itself, reset it explicitly rather than guessing:
sudo -u postgres psql -c "ALTER ROLE app_user WITH PASSWORD 'new_secure_password';"
Check pg_hba.conf to confirm the authentication method matches how you're connecting:
sudo cat /etc/postgresql/16/main/pg_hba.conf | grep -v "^#"
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
The local line using peer authentication means connecting via a Unix socket requires your OS username to match the PostgreSQL role name β a password won't work at all for that connection type. If you need password auth locally too, either connect via TCP explicitly:
psql -U app_user -d mydb -h 127.0.0.1
Or change the local line's method to md5 or scram-sha-256 if password-based local connections are actually what you need:
local all all scram-sha-256
After any change to pg_hba.conf, reload PostgreSQL for it to take effect (a full restart isn't necessary):
sudo systemctl reload postgresql
Still Not Working?
If you recently upgraded PostgreSQL and older clients started failing, check whether the role's password hash uses the newer scram-sha-256 format that some older client libraries can't handle:
SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'app_user';
If the client library genuinely can't be upgraded, you can revert that specific role to the older md5 method as a compatibility measure, though upgrading the client is the more durable fix long-term:
SET password_encryption = 'md5';
ALTER ROLE app_user WITH PASSWORD 'your_password';
Also double-check no environment variable is silently overriding your intended password β PGPASSWORD takes precedence over an interactively typed password and is easy to forget you've set in a shell session:
echo $PGPASSWORD
It's also worth understanding pg_hba.conf's matching order, since PostgreSQL uses the first matching line, top to bottom, and stops there β it doesn't try every applicable rule and pick the best one. If you have multiple lines that could plausibly apply to your connection, an earlier, more restrictive rule can silently shadow a later, more permissive one you actually intended to use:
# This ordering means the first line always wins for local TCP connections,
# and the second line below it is effectively dead code
host all all 127.0.0.1/32 reject
host all all 127.0.0.1/32 scram-sha-256
After making any change to pg_hba.conf, it's worth reviewing the full file from top to bottom rather than just the line you edited, to confirm nothing earlier in the file is unintentionally intercepting the connection before your intended rule is ever reached. A quick way to see exactly which rule PostgreSQL is applying to a given connection attempt is checking the server log immediately after a failed attempt, since recent PostgreSQL versions log which pg_hba.conf line matched:
tail -5 /var/log/postgresql/postgresql-16-main.log