SCRAM Authentication
SCRAM-SHA-256 is PostgreSQL's default password authentication. The server stores a salted verifier, not the plaintext password, and the client proves knowledge through a challenge-response protocol. Pair SCRAM with TLS (hostssl) for production; add LDAP or cloud IAM when central identity is required.
-- Server default for new passwords (postgresql.conf)
-- password_encryption = scram-sha-256
CREATE ROLE app_api LOGIN PASSWORD 'vault-generated-secret' ;
# pg_hba.conf
hostssl orders app_api 10.20.0.0/16 scram-sha-256
# Verify stored hash type
psql -c "SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'app_api';"
# rolpassword starts with SCRAM-SHA-256$
When to reach for this:
All password-based roles in PostgreSQL 14+
Migrating off md5 during upgrade or compliance audit
Baseline before LDAP/SSO - even federated flows often map to a DB role
Migrate legacy md5 role to SCRAM and enforce on connections.
-- Confirm encryption setting
SHOW password_encryption; -- scram-sha-256
-- Re-set password to regenerate SCRAM verifier
ALTER ROLE app_api PASSWORD 'new-rotated-secret-from-vault' ;
-- Confirm verifier format
SELECT rolname,
left (rolpassword, 14 ) AS hash_prefix
FROM pg_authid
WHERE rolname = 'app_api' ;
# pg_hba.conf - no md5 for application roles
hostssl orders app_api 10.20.0.0/16 scram-sha-256
# Client must use libpq/psql/driver with SCRAM support (PostgreSQL 10+ clients)
psql "host=db.internal dbname=orders user=app_api sslmode=verify-full"
What this demonstrates:
ALTER ROLE ... PASSWORD regenerates SCRAM verifier when password_encryption = scram-sha-256
pg_hba method must be scram-sha-256, not md5
Old JDBC/psql without SCRAM will fail fast (upgrade clients)
Client requests authentication for user.
Server sends salt and iteration count from stored verifier.
Client derives proof from password + salt without sending password on wire.
Server verifies proof against pg_authid.rolpassword.
# pg_hba.conf - LDAP bind (directory validates password)
hostssl all +ldap_users 10.20.0.0/16 ldap ldapserver=ldap.corp.internal ldapbasedn="dc=corp,dc=internal"
-- Map LDAP group to role (pattern)
CREATE ROLE ldap_users;
GRANT app_api TO ldap_users; -- inherit app privileges via GRANT
LDAP delegates password checks to Active Directory/OpenLDAP. PostgreSQL still authorizes with GRANT. Use LDAPS or TLS to directory.
Pattern How it works RDS IAM auth Short-lived token as password; no static secret in app Cloud SQL IAM Connector or IAM DB auth with token Azure AD Often via proxy or AAD-enabled flexible server
Static SCRAM passwords remain common for migration roles and third-party BI tools without IAM support.
-- Force SCRAM for role password changes
ALTER SYSTEM SET password_encryption = 'scram-sha-256' ;
SELECT pg_reload_conf();
-- List roles still on md5
SELECT rolname FROM pg_authid
WHERE rolpassword LIKE 'md5%' ;
md5 verifiers after upgrade - login works only if pg_hba still allows md5. Fix: re-ALTER ROLE PASSWORD for each login role.
PgBouncer auth_query mismatch - pooler must support SCRAM to backend. Fix: PgBouncer 1.21+ with auth_type = scram-sha-256.
** JDBC old versions** - SCRAM unsupported. Fix: PostgreSQL JDBC 42.2.0+.
Shared password in connection pool config - rotation requires coordinated deploy. Fix: vault sidecar or IAM tokens per instance.
LDAP without TLS - directory password exposed. Fix: LDAPS and hostssl to Postgres.
Logging failed auth at log_statement = all - noise only; use connection logs and external SIEM correlation.
Alternative Use When Don't Use When Certificate auth Fixed service mesh identities Human users and laptops GSSAPI/Kerberos Enterprise SSO already on Kerberos Cloud-native k8s without AD IAM token auth AWS/GCP managed Postgres Self-hosted without token infra trust (local socket) psql admin on DB host onlyAny TCP connection
Is SCRAM better than md5?
Yes. md5 is a single hash with no per-user salt in the protocol sense; SCRAM uses salted verifiers and modern iteration.
Do I need to change app code for SCRAM?
Only if the driver is ancient. Modern pg, JDBC, Npgsql, and asyncpg support SCRAM transparently.
How do I rotate SCRAM passwords?
ALTER ROLE PASSWORD, update vault/secret, rolling restart app pods. No server restart required.
Can PgBouncer store SCRAM?
Use auth_type = scram-sha-256 and auth_file or auth_query matching server verifiers.
What about replication users?
Replication roles use same SCRAM rules; set password and hostssl ... replication in pg_hba.
LDAP vs SCRAM for humans?
LDAP for employees; SCRAM service accounts for apps and CI migrator roles.
Does RDS enforce SCRAM?
RDS supports SCRAM for password auth. IAM auth is separate and preferred for AWS-native apps.
Failed auth debugging?
Check pg_hba order, role LOGIN attribute, SSL requirement, and client SCRAM support.
Password in connection URI?
Avoid in shell history. Use .pgpass with 0600 perms or env from secret manager.
Multi-factor on Postgres?
Not built-in for SQL login. MFA at VPN/SSO layer or IAM-based auth for managed offerings.
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+ , PgBouncer 1.x , Patroni 3.x , and PostGIS 3.5+ .
Y29kZWd1aWRlcy5pb3xjZ2lvMTA2NXwyMDI2MDc=