How to Fix PostgreSQL 'password authentication failed for user postgres' with Peer Auth (pg_hba.conf)
Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–10 mins
TL;DR
- What broke:
pg_hba.confhaspeerauth for thepostgresuser orlocalsocket connections, so PostgreSQL validates the OS username against the DB username — your password is never even checked. - How to fix it: Change the auth method in
pg_hba.conffrompeertomd5orscram-sha-256for the relevant connection type, thenpg_ctlcluster reloadorsystemctl reload postgresql. - Fast path: Use our Client-Side Sandbox below to auto-refactor your
pg_hba.conf— paste it in, get the corrected output without sending your config to any server.
The Incident (What Does This Error Mean?)
FATAL: password authentication failed for user "postgres"
Misleading error. The real problem is not a wrong password. PostgreSQL never attempted password validation. The peer authentication method maps the OS-level Unix socket user to the database user. If you're connecting as ubuntu, ec2-user, or any non-postgres OS user, the handshake fails before your password is read.
Immediate consequence: Every application, migration script, or DBA session using a password over a local socket is dead. psql -U postgres -W will loop-fail regardless of what password you type.
The Attack Vector / Blast Radius
This is a misconfigured trust boundary, not a brute-force vulnerability — but the blast radius is severe:
- Full application outage: Any app connecting via
localhostor Unix socket withpassword/md5in its DSN is locked out. - Migration failures: Tools like Flyway, Liquibase, Alembic, and
pg_restorewill fail silently or with cryptic errors if they swallow the FATAL. - Operator lockout during incident: The worst time to discover this is during a prod incident when you need emergency DB access.
- Silent misconfiguration drift: If
peerwas intentional for thepostgressuperuser (it often is, as a security control), and someone changed the OS user context in a container or systemd unit, auth silently breaks with no alerting.
Security note: peer auth is actually more secure than password auth for local superuser access — it means only the OS postgres user can connect as DB postgres. If you're disabling it, ensure you have compensating controls (strong passwords, scram-sha-256, firewall rules).
How to Fix It
Step 1 — Find your pg_hba.conf
psql -U postgres -c 'SHOW hba_file;'
# or
sudo -u postgres psql -c 'SHOW hba_file;'
# Common paths:
# /etc/postgresql/15/main/pg_hba.conf (Debian/Ubuntu)
# /var/lib/pgsql/data/pg_hba.conf (RHEL/CentOS)
Basic Fix — Change peer to scram-sha-256
# pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
-local all postgres peer
+local all postgres scram-sha-256
# Keep peer for other local OS-matched users if needed:
local all all peer
Then reload (no restart needed):
# Debian/Ubuntu
sudo systemctl reload postgresql
# RHEL/CentOS
sudo systemctl reload postgresql-15
# Or via pg_ctlcluster
sudo pg_ctlcluster 15 main reload
Set the password if not already set:
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'your-strong-password';"
Enterprise Best Practice — Least Privilege + scram-sha-256
Never use the postgres superuser for application connections. The correct production pattern:
# pg_hba.conf
# Superuser: keep peer (OS-level protection, no password needed)
+local all postgres peer
# App user: scram-sha-256 over TCP only, bound to specific DB
-local all appuser md5
+host myappdb appuser 127.0.0.1/32 scram-sha-256
# Reject everything else locally
+local all all reject
Why scram-sha-256 over md5: MD5 is broken. scram-sha-256 (default since PG 14) uses a challenge-response protocol — the password hash is never transmitted. Set it as default:
# postgresql.conf
-password_encryption = md5
+password_encryption = scram-sha-256
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Checkov — Scan pg_hba.conf in IaC
If you're templating pg_hba.conf via Ansible, Terraform, or Helm, add a custom Checkov check:
# checkov custom check: no md5 in pg_hba
from checkov.common.models.enums import CheckResult
# Flag any pg_hba line using 'md5' or 'trust'
if 'md5' in line or 'trust' in line:
return CheckResult.FAILED
2. Ansible Lint / Template Validation
# In your Ansible pg_hba template task, assert:
- name: Assert scram-sha-256 is enforced
assert:
that:
- "'peer' in pg_hba_postgres_method or 'scram-sha-256' in pg_hba_postgres_method"
- "'trust' not in pg_hba_all_method"
- "'md5' not in pg_hba_all_method"
3. Docker/Container Entrypoint Guard
If running PostgreSQL in containers, the POSTGRES_HOST_AUTH_METHOD env var controls this:
# docker-compose.yml
environment:
- POSTGRES_HOST_AUTH_METHOD: trust
+ POSTGRES_HOST_AUTH_METHOD: scram-sha-256
+ POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256"
4. Integration Test in Pipeline
#!/bin/bash
# ci/test-db-auth.sh — fail the pipeline if peer auth leaks to app user
result=$(PGPASSWORD=testpass psql -h 127.0.0.1 -U appuser -d myappdb -c 'SELECT 1;' 2>&1)
if echo "$result" | grep -q 'peer'; then
echo "FATAL: peer auth detected on app user connection — pg_hba.conf misconfigured"
exit 1
fi