PostgreSQL 19 makes three authentication changes: a password-expiration warning, a warning after successful MD5 authentication, and removal of native RADIUS support. Together they improve password management, flag legacy methods, and drop the insecure RADIUS/UDP implementation.
In this post, we verify that the password-expiration warning and the MD5 authentication warning work correctly and reliably. It covers warning behavior, message delivery to clients, configuration options, and compatibility with existing authentication workflows. It also presents some thoughts of the developers w.r.t dropping the native RADIUS support.
1. Password Expiration Warning
The server warns users when their password is close to expiring, so they can change it in time. The backend sends the message, but the client application must display it.
2. Warning Upon Successful MD5 Authentication
A warning now appears when a login succeeds with an MD5 password. It helps administrators find users and applications still on MD5 and move them to SCRAM-SHA-256 gradually.
3. Time to Drop RADIUS Support?
Native RADIUS is removed because of the security problems with RADIUS over UDP, including BlastRADIUS, and the cost of maintaining it. Users who still need RADIUS can use PAM-based RADIUS instead.
Feature 1: Password Expiration Warning
The Problem
When a PostgreSQL role has a VALID UNTIL date set, users get zero advance notice before their password expires. They only find out when login suddenly fails:
FATAL: password authentication failed for user "foo"
The real cause: password expiration is buried in the server log:
DETAIL: User "foo" has an expired password.
This is especially painful for application service accounts that break silently at 2 AM.
The Solution: password_expiration_warning_threshold
A new server-side GUC was introduced that emits a WARNING at login when a password is within a configurable time of expiring.
$ psql -h localhost -U test -d postgres
Password for user test:
WARNING: password will expire soon
DETAIL: The password for role "test" will expire in 3 days.
psql (19devel)
The warning appears for all clients, not just psql and is also written to the server logs for audit purposes.
Configuration
# postgresql.conf
password_expiration_warning_threshold = 7d # default; 0 = disabled
| Setting | Meaning |
|---|---|
7d (default) | Warn 7 days before expiry |
24h | Warn 24 hours before expiry |
0 | Disable warnings entirely |
INT_MAX | No upper limit — set to whatever suits your policy |
The GUC is PGC_SIGHUP, so no restart is required to change it.
What the Warning Looks Like
The message uses the largest meaningful time unit, with proper singular/plural forms:
DETAIL: The password for role "alice" will expire in 3 days.
DETAIL: The password for role "bob" will expire in 2 hours.
DETAIL: The password for role "carol" will expire in 15 minutes.
DETAIL: The password for role "dave" will expire in less than 1 minute.
Key Design Decisions
The patch went through 17 versions before being committed. Here’s how the major design questions were resolved:
Server-side or client-side?
Original idea: A psql-only variable \set PASSWORD_EXPIRE_WARNING 7.
Problems:
(1) Not everyone uses psql.
(2) psql can’t reliably tell which auth method was actually used.
Decision: Server-side — all clients benefit automatically.
Show days, hours, or an exact timestamp?
| Option | Example | Verdict |
|---|---|---|
| Days only | expires in 3 day(s) | “(s)” anti-pattern; poor plurals |
| Days + Hours | expires in 2 days and 7 hours | Complex; multi-plural problem |
| Largest unit only | expires in 3 days / expires in 2 hours | Chosen |
| Exact timestamp | expires at 2026-02-05 16:56 | Requires mental math; timezone confusion |
GUC name debate
| Candidate | Problem |
|---|---|
password_expire_warning | Too terse |
password_warning_threshold | Ambiguous — warning about what? |
password_expiration_warning_threshold | Chosen — descriptive and unambiguous |
Default on or off?
Off-by-default is pretty much guaranteed to not help most people. — Tom Lane
Decision: Default ON at 7 days. Set to 0 to disable.
Implementation Highlights
- Warnings emit at the end of
InitPostgres()— after the connection is fully established — so no information is leaked if an error occurs mid-setup. - Works for all backend types that authenticate with a password, including logical replication connections.
VALID UNTIL 'infinity'is silently skipped — no false warnings.- A new connection warnings infrastructure on
Portwas introduced, reusable by future warnings (see Feature 2 below).
NOTE
Long-lived connections are not yet covered. A user who connects with >7 days remaining won’t receive a mid-session reminder. The goaway patch (a separate ongoing effort) may address periodic in-session notifications in the future.
Feature 2: Warning on MD5 Password Authentication
The Problem
MD5 password hashing has been considered cryptographically weak for years. PostgreSQL already supports scram-sha-256 as the modern replacement, but many users and applications are still unknowingly using MD5. There is currently no feedback to alert users or administrators that MD5 is in use.
The Solution: A WARNING at MD5 Login
Building directly on the connection warnings infrastructure introduced in Feature 1, a new warning is emitted whenever a role successfully authenticates using an MD5-hashed password:
$ psql -h localhost -U legacy_user -d mydb
Password for user legacy_user:
WARNING: authentication with MD5-hashed passwords is deprecated
DETAIL: The role "legacy_user" authenticated using an MD5-hashed password. Consider migrating to scram-sha-256.psql (19devel)
The warning also appears in server logs, giving DBAs visibility into which accounts still need migration.
Why This Matters
MD5 is broken for password hashing for several reasons:
| Issue | Detail |
|---|---|
| Collision attacks | MD5 is cryptographically broken |
| No salting per-login | Makes offline dictionary attacks easier |
| No iteration | Fast to crack with modern hardware |
| Protocol leakage | MD5 auth sends a hash that can be replayed |
scram-sha-256 Addresses all of these: salted, iterated, and challenge-response based (no hash is sent over the wire).
How to Migrate Away from MD5
Step 1 — Find all MD5 accounts:
SELECT rolname FROM pg_authid WHERE rolpassword LIKE 'md5%';
Step 2 — Require SCRAM in postgresql.conf:
password_encryption = scram-sha-256
Step 3 — Have each user reset their password:
ALTER ROLE legacy_user PASSWORD 'new_secure_password';
Step 4 — Enforce SCRAM in pg_hba.conf:
host all all 0.0.0.0/0 scram-sha-256
WARNING
Changing password_encryption does not automatically re-hash existing passwords. Each role must reset their password for the new hash type to take effect.
Relationship to Feature 1
This feature deliberately reuses the same connection warnings infrastructure introduced for password expiration warnings. Nathan Bossart designed it as a generic list of (message, detail) pairs stored on Port, making it easy to stack multiple warnings:
WARNING: password will expire soon
DETAIL: The password for role "old_user" will expire in 2 days.
WARNING: authentication with MD5-hashed passwords is deprecated
DETAIL: The role "old_user" authenticated using an MD5-hashed password.
This composable design means future connection-time warnings can be added cleanly without touching the core plumbing again.
Feature 3: Drop native RADIUS Support
What Is Being Proposed?
Rather than invest in a RADIUS/TLS client for a hypothetical user, the proposal is:
| Action | Scope |
|---|---|
| Add a deprecation notice in the documentation | PostgreSQL 14–18 (back-branches) |
| Remove native RADIUS client code entirely | PostgreSQL 19 (HEAD) |
Clean up Windows select() and non-thread-safe UDP hacks | PostgreSQL 19 (HEAD) |
Community Consensus
| Contributor | View |
|---|---|
| Tom Lane | Remove from HEAD; preserve back-branches until zero-user status confirmed |
| Jacob Champion | +1 to removal; add a back-branch WARNING as a kindness to users |
| Álvaro Herrera | Add WARNING to gauge real-world usage before full removal |
| Aleksander Alekseev | +1, perfectly reasonable |
| Thomas Munro | Proposed the removal; created wiki migration guide |
Three patches were submitted:
0001— Mark RADIUS deprecated in docs0002— Drop RADIUS authentication support (34 KB of code removed)0003— Remove obsolete Windowsselect()and UDP infrastructure
The Migration Path: RADIUS via PAM
You can already do RADIUS better using PAM with the module maintained by the FreeRADIUS project. Here’s a complete setup:
/etc/pam.d/postgresql
#%PAM-1.0auth required pam_radius.so require_message_authenticatoraccount required pam_permit.so
/etc/radius.conf — point PAM at your RADIUS server
# Server[:port] SharedSecret Timeout Retries
127.0.0.1 shared_secret 3 3
pg_hba.conf — tell PostgreSQL to use PAM
host all all 127.0.0.1/32 pam pamservice=postgresql
Why PAM Is Better
| Concern | Native RADIUS | PAM + pam_radius |
|---|---|---|
| Maintained by | PostgreSQL team | FreeRADIUS project |
| Supports RADIUS/TLS | No | Yes (via config) |
| BlastRADIUS safe | Only with mitigation patch | Yes (require_message_authenticator) |
| Does it require fixing within Postgres? | Yes | No |
Security Impact and Recommendations
These changes encourage administrators to adopt stronger authentication practices and identify configurations that require attention.
- Password expiration: Configure an appropriate warning period and ensure that client applications display server notices to users.
- MD5 authentication: Identify roles and applications using legacy password authentication and plan a migration to SCRAM-SHA-256. Verify client compatibility before changing authentication settings.
- RADIUS authentication: Review existing
pg_hba.confconfigurations before upgrading. If RADIUS is required, evaluate PAM-based authentication or another supported approach for the operating system and deployment.
Administrators should test these changes in a development or staging environment before applying them to production systems.
Conclusion:
PostgreSQL 19 takes steps toward stronger authentication security by improving password-expiration awareness, warning about successful MD5 authentication, and removing native RADIUS support that relied on the insecure RADIUS/UDP variant.
Together, these changes help administrators identify outdated authentication configurations and plan migrations to more secure alternatives. Careful testing is essential to confirm warning behavior, client compatibility, configuration handling, and the reliability of authentication workflows. By validating these changes before deployment, teams can improve their security posture while minimizing disruption to existing applications.
