TL;DR
You hit FATAL: password authentication failed for user "your_user". Run through this checklist before digging deeper:
- Wrong password โ reset it with
ALTER USER your_user WITH PASSWORD 'newpassword'; pg_hba.confauth method mismatch โ alignmd5vsscram-sha-256- User has no password set โ set one explicitly
- Connection string typo โ double-check your app config
What's Actually Happening
PostgreSQL rejected the password your client sent. Three distinct causes lead here, and each needs a different fix. Blindly resetting the password only solves one of them โ figure out which case you're in first.
Reason 1: The Password Is Simply Wrong
Most common cause. The password in your connection string doesn't match what PostgreSQL has stored for that user. This also bites you after server migrations โ for example, if you pg_dumpall'd roles but then manually reset the password on the new server while the app still carries the old value in its config.
Reason 2: Authentication Method Mismatch in pg_hba.conf
pg_hba.conf controls how each connection is authenticated. If the file says md5 but the password was stored as scram-sha-256 (the default since PostgreSQL 14), the login fails even when the password is 100% correct. The reverse works the same way โ wrong direction, same error.
Reason 3: User Has No Password or Doesn't Exist
A user created with CREATE USER myuser; โ no password clause โ can't authenticate via password-based methods. PostgreSQL returns this same opaque error instead of something like "no password set," which is frustrating but consistent.
Fix Approaches
1. Reset the User's Password
Connect as the postgres superuser. On Linux this usually works via peer authentication:
sudo -u postgres psql
Then reset the password:
ALTER USER your_user WITH PASSWORD 'your_new_password';
Exit with \q and test the connection:
psql -U your_user -h 127.0.0.1 -d your_database -W
The -W flag forces a password prompt, confirming you're testing the actual credential rather than falling through to peer auth silently.
2. Fix the pg_hba.conf Authentication Method
First, find your pg_hba.conf location:
sudo -u postgres psql -c "SHOW hba_file;"
On Ubuntu/Debian with PostgreSQL 15, this is typically /etc/postgresql/15/main/pg_hba.conf. Open it and find the line matching your connection type:
# TYPE DATABASE USER ADDRESS METHOD
host all all 127.0.0.1/32 md5
Running PostgreSQL 14+ with default settings? The stored hash is scram-sha-256. Change the method to match:
host all all 127.0.0.1/32 scram-sha-256
Need to support older clients that only speak md5? Keep the method as md5 but re-hash the password in md5 format explicitly:
-- Run as postgres superuser:
SET password_encryption = md5;
ALTER USER your_user WITH PASSWORD 'your_password';
After any pg_hba.conf change, reload PostgreSQL โ no full restart required:
sudo systemctl reload postgresql
3. Verify the User Exists and Has a Password Set
Check whether the user exists at all:
sudo -u postgres psql -c "\du your_user"
No output means the user doesn't exist. Create them:
CREATE USER your_user WITH PASSWORD 'your_password';
Already exists but you're not sure if a password is stored? Query pg_shadow (superuser only):
SELECT usename, passwd IS NOT NULL AS has_password
FROM pg_shadow
WHERE usename = 'your_user';
If has_password returns f, run the ALTER USER command above to set one.
4. Quick Debug: Temporarily Use Trust Authentication
Not sure whether the problem is the password or the auth method? You can temporarily bypass password checks for your user on localhost. Add this line to pg_hba.conf above the existing host lines:
host all your_user 127.0.0.1/32 trust
Reload and connect. If it works, the user and database config are fine โ the issue is purely the password or hash method. Remove this line immediately after testing. Trust auth in production is a serious security hole.
Verify the Fix Worked
Test with an explicit password prompt:
psql -U your_user -h 127.0.0.1 -d your_database -W
For app connections, check your connection pool logs. PgBouncer, Sequelize, and Django's connection layer all pick up fresh credentials on the next attempt โ you usually don't need to restart the whole app, just wait for the pool to cycle. If you store the connection string in environment variables, confirm the updated value is actually loaded into the running process, not just written to disk.
You can also test from inside your app's environment directly:
psql "postgresql://your_user:your_password@127.0.0.1:5432/your_database"
Tips for Preventing This
The most common repeat offender in team setups: someone sets a database password once, doesn't document it, and later a different developer grabs it from Slack or a wiki โ typos happen, values drift out of date. Set strong random passwords from day one and store them in a secrets manager or a gitignored .env file, not in chat.
Need a random password right now? ToolCraft's Password Generator runs entirely in your browser with no server-side processing โ useful when you want something genuinely random without installing anything.
One more thing to watch after major version upgrades: jumping from PostgreSQL 13 to 16 changes the default password_encryption from md5 to scram-sha-256. Existing hashes carry over as-is, but any password reset after the upgrade uses the new format. You end up with a mix of hash types and a guaranteed auth mismatch for whoever gets reset first. Run an audit after any major version jump to catch this early.

