PostgreSQL Error Guide: 'password authentication failed for user' Login Fails
Fix PostgreSQL 'FATAL: password authentication failed for user': diagnose wrong passwords, pg_hba.conf md5/scram method mismatches, missing LOGIN roles, and encryption mismatches.
- #postgres
- #troubleshooting
- #errors
- #authentication
Overview
PostgreSQL returns this error when a client presents credentials that the server cannot verify against the matching pg_hba.conf rule. The connection reached the server, found a host-based authentication line, attempted a password method (md5, scram-sha-256, or password), and the password check failed. Authentication, not connectivity, is the problem here.
You will see this in the client output and the PostgreSQL log:
FATAL: password authentication failed for user "appuser"
It occurs at login time, after the network connection succeeds and a pg_hba.conf rule is matched. A subtle point: PostgreSQL deliberately returns the same message whether the password is wrong, the role lacks a stored password, or the stored password is the wrong encryption type — so the obvious “wrong password” is only one of several real causes.
Symptoms
- Login fails with
FATAL: password authentication failed for user "<name>". - The same credentials may work from one host or database but not another.
- The server log records the failed line with the auth method that was attempted.
- A role that worked yesterday fails after a
pg_hba.conforpassword_encryptionchange.
psql "host=db.internal user=appuser dbname=appdb" -c 'select 1'
psql: error: connection to server at "db.internal" (10.0.4.10), port 5432 failed:
FATAL: password authentication failed for user "appuser"
sudo tail -5 /var/lib/pgsql/data/log/postgresql.log
LOG: connection received: host=10.0.4.30 port=51884
FATAL: password authentication failed for user "appuser"
DETAIL: Connection matched pg_hba.conf line 92: "host appdb appuser 10.0.4.0/24 md5"
The DETAIL line naming the matched pg_hba.conf rule is the single most useful clue — it tells you which rule and method are in play.
Common Root Causes
1. The password is simply wrong
The most common cause: the client is sending a stale or mistyped password, often from an environment variable, .pgpass, or a secret that drifted from what the role actually has.
# Confirm which password source the client is using
echo "PGPASSWORD set: ${PGPASSWORD:+yes}"
grep -H "db.internal" ~/.pgpass 2>/dev/null
PGPASSWORD set: yes
/home/deploy/.pgpass:db.internal:5432:appdb:appuser:Old-Passw0rd
A .pgpass entry with an outdated password silently overrides what you think you are typing. Reset the role password to a known value and retry to rule this in or out.
2. pg_hba.conf method mismatch (md5 vs scram-sha-256)
If the role’s password is stored as SCRAM but the matching pg_hba.conf line specifies md5 (or vice versa), authentication can fail depending on versions and the stored format.
grep -nE '^(host|local).*appuser' /var/lib/pgsql/data/pg_hba.conf
92:host appdb appuser 10.0.4.0/24 md5
93:host all all 0.0.0.0/0 scram-sha-256
If the password was stored under scram-sha-256 but line 92 demands md5, the md5 challenge cannot validate a SCRAM verifier. Align the method to how the password is actually stored (see cause 6).
3. The role does not exist or has no LOGIN privilege
A typo in the username, or a role created with NOLOGIN (the default for CREATE ROLE without LOGIN), produces the same authentication failure.
SELECT rolname, rolcanlogin
FROM pg_roles
WHERE rolname = 'appuser';
rolname | rolcanlogin
---------+-------------
appuser | f
(1 row)
rolcanlogin = f means the role cannot log in at all. An empty result means the role name is wrong. Grant login with ALTER ROLE appuser LOGIN;.
4. The role has no password set (NULL password)
A role created without a password, or one whose password was cleared, has a NULL rolpassword. Any password the client sends will fail the check.
SELECT rolname,
rolpassword IS NULL AS password_is_null,
left(rolpassword, 14) AS pw_prefix
FROM pg_authid
WHERE rolname = 'appuser';
rolname | password_is_null | pw_prefix
---------+------------------+----------------
appuser | t |
(1 row)
password_is_null = t means no password is stored; set one with ALTER ROLE appuser PASSWORD '...';. (Reading pg_authid requires superuser.)
5. Wrong database in the connection string matches a different hba rule
pg_hba.conf rules are matched on database name as well as user and source address. Connecting to a different database can match a stricter rule (or a reject line) and fail, even with correct credentials.
grep -nE '^(host|local)' /var/lib/pgsql/data/pg_hba.conf | head
40:local all all peer
55:host appdb appuser 10.0.4.0/24 md5
56:host reportdb appuser 10.0.4.0/24 reject
70:host all all 0.0.0.0/0 scram-sha-256
Connecting appuser to reportdb hits the reject on line 56, while appdb works on line 55. The error looks identical to a bad password but is really a rule-ordering/database issue.
6. password_encryption mismatch (scram stored vs md5 expected)
When password_encryption is changed (the modern default is scram-sha-256), existing passwords keep their old encoding until the password is reset. A role with an md5-encoded password under a scram-sha-256 hba line, or the reverse, fails to authenticate.
SHOW password_encryption;
SELECT rolname,
CASE
WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram-sha-256'
WHEN rolpassword LIKE 'md5%' THEN 'md5'
ELSE 'plain/none'
END AS stored_as
FROM pg_authid
WHERE rolname = 'appuser';
password_encryption
---------------------
scram-sha-256
rolname | stored_as
---------+-----------
appuser | md5
(1 row)
The server now expects SCRAM, but appuser still has an md5 verifier stored. Re-setting the password under the new password_encryption regenerates the correct verifier.
Diagnostic Workflow
Step 1: Read the server log for the matched hba line
sudo grep -A1 'password authentication failed' \
/var/lib/pgsql/data/log/postgresql.log | tail -4
The DETAIL: Connection matched pg_hba.conf line N tells you exactly which rule and method are being applied. Everything else flows from that line.
Step 2: Verify the role exists and can log in
SELECT rolname, rolcanlogin, rolvaliduntil
FROM pg_roles
WHERE rolname = 'appuser';
A missing row (wrong name), rolcanlogin = f (NOLOGIN), or a past rolvaliduntil (expired password) each cause the same error. Fix with ALTER ROLE appuser LOGIN; or ALTER ROLE appuser VALID UNTIL 'infinity';.
Step 3: Check how the password is stored vs what hba expects
SHOW password_encryption;
SELECT rolname,
rolpassword IS NULL AS is_null,
CASE WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram'
WHEN rolpassword LIKE 'md5%' THEN 'md5' ELSE 'none' END AS stored_as
FROM pg_authid WHERE rolname = 'appuser';
A NULL password, or a stored_as that does not match the hba line’s method, is the cause. Reset the password (Step 5) to regenerate a matching verifier.
Step 4: Confirm the database and source address match the intended rule
grep -nE '^(host|local).*(appuser|all)' /var/lib/pgsql/data/pg_hba.conf
Ensure the database and address columns of the intended rule actually cover the database name and client IP you are connecting from, and that no earlier reject/stricter line shadows it. Rules are evaluated top to bottom; the first match wins.
Step 5: Reset the password and reload, then retry
-- As a superuser; this re-encodes under the current password_encryption
ALTER ROLE appuser WITH LOGIN PASSWORD 'NewStr0ng-Pass';
# After editing pg_hba.conf, reload (no restart needed for hba changes)
sudo -u postgres psql -c 'SELECT pg_reload_conf();'
Resetting the password under the active password_encryption is the single fix that resolves NULL-password, encryption-mismatch, and stale-credential cases at once.
Example Root Cause Analysis
After upgrading the cluster, the reporting service starts failing every connection with FATAL: password authentication failed for user "reporter", even though the password in its secret is unchanged and correct.
The server log points at the rule:
FATAL: password authentication failed for user "reporter"
DETAIL: Connection matched pg_hba.conf line 70: "host all all 0.0.0.0/0 scram-sha-256"
The matched line uses scram-sha-256. Checking how the password is actually stored:
SHOW password_encryption;
SELECT rolname,
CASE WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram'
WHEN rolpassword LIKE 'md5%' THEN 'md5' ELSE 'none' END AS stored_as
FROM pg_authid WHERE rolname = 'reporter';
password_encryption
---------------------
scram-sha-256
rolname | stored_as
----------+-----------
reporter | md5
The upgrade flipped password_encryption to scram-sha-256 and the hba line to match, but reporter’s password was set years ago and is still stored as an md5 verifier. An md5 hash cannot satisfy a SCRAM exchange, so every login fails — the credential itself was never wrong.
Fix: re-set the password (the value can be identical) so it is re-encoded under SCRAM:
ALTER ROLE reporter WITH PASSWORD 'same-secret-value-as-before';
SELECT CASE WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram' ELSE 'md5' END
FROM pg_authid WHERE rolname = 'reporter';
case
-------
scram
With a SCRAM verifier now stored, the reporting service authenticates against line 70 and reconnects normally.
Prevention Best Practices
- Standardize on
scram-sha-256forpassword_encryptionandpg_hba.conf, and re-set every role’s password once after switching so no stalemd5verifiers linger. - Keep
pg_hba.confrules ordered from most specific to least specific, and avoid broadrejectlines that can shadow legitimate database-specific access. - Store credentials in one source of truth (a secret manager) and avoid duplicating them in
.pgpass, environment variables, and config files where they drift apart. - Always create application roles with explicit
LOGIN PASSWORD ...and aVALID UNTILpolicy you actually monitor, so expired or NOLOGIN roles surface before users hit them. - Test the full connection string (host, database, user) from the real client host after any hba change, since the matched rule depends on all three.
- When triaging a failure, the server log’s
DETAIL: Connection matched pg_hba.conf line Nis authoritative — the free incident assistant can map that log line straight to the misconfigured rule or method.
Quick Command Reference
# Server log: which hba line matched?
sudo grep -A1 'password authentication failed' \
/var/lib/pgsql/data/log/postgresql.log | tail -4
# Inspect hba rules for a user
grep -nE '^(host|local).*(appuser|all)' /var/lib/pgsql/data/pg_hba.conf
# Reload after editing pg_hba.conf (no restart needed)
sudo -u postgres psql -c 'SELECT pg_reload_conf();'
-- Does the role exist and can it log in?
SELECT rolname, rolcanlogin, rolvaliduntil FROM pg_roles WHERE rolname = 'appuser';
-- How is the password stored vs what hba expects? (superuser only)
SHOW password_encryption;
SELECT rolname, rolpassword IS NULL AS is_null,
CASE WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram'
WHEN rolpassword LIKE 'md5%' THEN 'md5' ELSE 'none' END AS stored_as
FROM pg_authid WHERE rolname = 'appuser';
-- Re-encode the password under the current password_encryption
ALTER ROLE appuser WITH LOGIN PASSWORD 'NewStr0ng-Pass';
Conclusion
FATAL: password authentication failed for user means the connection reached a pg_hba.conf rule but the password check failed — and PostgreSQL deliberately reuses this message for several distinct causes. The usual root causes:
- The password is simply wrong or stale (often a drifted
.pgpass/secret). - A
pg_hba.confmethod mismatch betweenmd5andscram-sha-256. - The role does not exist or was created
NOLOGIN. - The role has no password set (NULL
rolpassword). - The connection’s database matches a stricter or
rejecthba rule. - A
password_encryptionchange left an md5 verifier under a SCRAM rule (or vice versa).
Start from the log’s DETAIL: Connection matched pg_hba.conf line N: it pins down the rule and method, after which verifying the role and re-setting the password under the current encryption resolves the large majority of cases. More PostgreSQL guides are in the Postgres category.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.