FATAL: Peer authentication failed for user "postgres" is PostgreSQL rejecting you on a rule that never looks at your password at all:
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed:
FATAL: Peer authentication failed for user "postgres"
If you have been resetting the postgres password and retrying, that is why nothing changes. Peer authentication compares your operating system user name against the database user name you asked for, and refuses when they differ. Your OS user is not postgres, so it stops there.
Why This Happens
PostgreSQL decides how to authenticate you by reading pg_hba.conf top to bottom and using the first line that matches your connection type, database, user, and source address. Two rules follow from that, and both cause confusion:
- There is no fall-through. If a line matches and authentication fails, PostgreSQL does not try later lines. Adding a
scram-sha-256line at the bottom of the file changes nothing if apeerline above it matched first. localandhostare different worlds. Omitting-huses a Unix socket and matcheslocallines. Adding-h localhostor-h 127.0.0.1forces TCP and matcheshostlines. These frequently have different auth methods, which is why one command works and the other fails.
A default pg_hba.conf contains something very close to this:
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
That first line is the one catching you.
Fix 1: Become the postgres User (No Config Change)
This is the correct answer most of the time, and it needs no edits:
sudo -u postgres psql
sudo -u postgres makes your OS user genuinely postgres for that command, which is exactly what peer authentication is checking. You are now in a session and can do everything else from there.
Fix 2: Create a Role Matching Your Own OS User
If you are a developer who wants psql to just work, do not weaken the postgres superuser — give yourself your own role named after your OS user, so peer authentication succeeds honestly:
sudo -u postgres createuser --interactive --pwprompt "$USER"
sudo -u postgres createdb -O "$USER" "$USER"
Answer no to superuser unless you genuinely need it. Now this works with no host flag and no password:
psql
This keeps peer authentication intact — which is a real security property on a shared host — while removing the friction.
Fix 3: Use a Password Method Over TCP
If an application must connect with a password (which is normal — applications have no OS user on the database host), give it a TCP path with a password method rather than changing the local rule.
First, make sure the role actually has a password:
sudo -u postgres psql -c "ALTER ROLE appuser WITH PASSWORD '<strong-password>';"
Then confirm a host line covers the source address with scram-sha-256. Locate the file:
sudo -u postgres psql -c "SHOW hba_file;"
Add a rule that is as narrow as the application needs — specific database, specific role, specific address:
# TYPE DATABASE USER ADDRESS METHOD
host appdb appuser 10.0.3.0/24 scram-sha-256
Place it above any broader line that would match first. Then reload — no restart, no dropped connections:
sudo -u postgres psql -c "SELECT pg_reload_conf();"
# or: sudo systemctl reload postgresql
Use scram-sha-256, not md5. MD5-based authentication is legacy and weaker; upstream has been steering installations to SCRAM for several major versions.
Fix 4: Read the Log and Stop Guessing
This is the step that turns a frustrating afternoon into a two-minute fix, and almost nobody does it. Every authentication failure writes a DETAIL line to the server log naming the exact pg_hba.conf line that matched:
FATAL: Peer authentication failed for user "postgres"
DETAIL: Connection matched pg_hba.conf line 85: "local all all peer"
There is no ambiguity left after reading that. The same mechanism explains the other confusing failures:
FATAL: password authentication failed for user "postgres"
DETAIL: User "postgres" has no password assigned.
FATAL: password authentication failed for user "ghost"
DETAIL: Role "ghost" does not exist.
Note the second one carefully: the client is told "password authentication failed" even when the role does not exist. That is deliberate, so an attacker cannot enumerate valid user names — but it means you can spend an hour resetting the password for a role that was never created. Only the log distinguishes them. See where PostgreSQL logs are stored for the path on your platform.
You can also inspect the rules as the server currently understands them, without opening the file:
SELECT line_number, type, database, user_name, address, auth_method
FROM pg_hba_file_rules
WHERE error IS NULL
ORDER BY line_number;
Match the line_number here to the one in the DETAIL message.
Related Failures You May Hit Next
FATAL: pg_hba.conf rejects connection for host ... user ... database ... — a line matched and its method was reject, or no line matched at all. Add or reorder a rule for that exact combination.
could not connect to server: Connection refused / Is the server running on that host and accepting TCP/IP connections? — this is not authentication. Either the server is down, or it is not listening on that address. Check listen_addresses in postgresql.conf and confirm the port:
sudo -u postgres psql -c "SHOW listen_addresses;"
sudo ss -ltnp | grep 5432
FATAL: role "yourname" does not exist — you connected over a socket with no -U, so psql defaulted to your OS user name, and there is no such role. Either pass -U or create the role as in Fix 2.
Do Not "Fix" This With trust
The most common bad advice for this error is to change peer to trust. Understand what that does: trust accepts the connection unconditionally, with no credentials whatsoever, so anyone who can reach that socket or port connects as any database user they name — including superusers, and including on a host line, anyone who can reach the port over the network. It does not fix authentication; it removes it. Every case above has a real fix that keeps authentication in place.
Verify the Fix
# Socket path (matches 'local' rules)
psql -U postgres -c "SELECT current_user, inet_server_addr(), version();"
# TCP path (matches 'host' rules) — prove both work as intended
psql -h 127.0.0.1 -U appuser -d appdb -c "SELECT current_user;"
Then confirm the role holds only the privileges it should:
\du appuser
Prevent It Coming Back
- Keep peer authentication for local administrative access. It is genuinely useful: it ties superuser access to OS-level access rather than to a password that can leak.
- Give each developer a role named after their OS user so routine work never needs the
postgresaccount. - Give applications their own least-privilege role over TCP with
scram-sha-256, scoped to one database. - Order
pg_hba.conffrom most specific to least specific, and remember that the first match wins with no fall-through. - Reload rather than restart after editing —
SELECT pg_reload_conf();applies changes without dropping sessions. - Check
pg_hba_file_rulesafter every edit to catch a syntax error before it locks you out.
Next Steps
- Where are PostgreSQL logs stored — the DETAIL line that names the matching rule
- Fix ERROR 1045 Access denied for the MySQL equivalent of this problem
- Fix "Login failed for user" 18456 for the SQL Server equivalent