Initializing Enclave...

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.conf has peer auth for the postgres user or local socket 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.conf from peer to md5 or scram-sha-256 for the relevant connection type, then pg_ctlcluster reload or systemctl 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 localhost or Unix socket with password/md5 in its DSN is locked out.
  • Migration failures: Tools like Flyway, Liquibase, Alembic, and pg_restore will 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 peer was intentional for the postgres superuser (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

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →