PostgreSQL 14 gets its final release on November 12. After that, no more fixes. Most teams I talk to have the upgrade on a list somewhere, and most of them are worried about the data: pg_upgrade, extensions, downtime.
The thing I'd worry about is smaller and later. It's a login failure that doesn't happen on upgrade day.
What changed in 14
PostgreSQL 14 changed the default of password_encryption from md5 to scram-sha-256. Every new password is stored as a SCRAM verifier unless you change that setting.
Here's the part that makes it sneaky. pg_upgrade keeps the MD5 hashes you already have. So you upgrade a 12 or 13 server to 14 (or straight to 18), your apps reconnect, everything is green, and you close the ticket.
Then, weeks later, someone rotates the app's database password. The new one is stored as SCRAM. A Java service that has been fine for years starts failing with:
org.postgresql.util.PSQLException: The authentication type 10 is not supported. Type 10 is SCRAM. The driver doesn't speak it. Moving with dump and restore doesn't avoid it: pg_dumpall --globals-only carries the MD5 hashes over as they are, so you're in the same position. It only fails on day one if you create the roles by hand on the new server, because then every password is set again.
Which pgjdbc versions can log in
pgjdbc added SCRAM-SHA-256 in 42.2.0, only in the Java 8+ build. But 42.2.0 shipped with a known issue: the SCRAM client classes weren't packaged, so it didn't actually work. 42.2.1 fixed that a week later.
So the floor is 42.2.1, and not a .jre6 or .jre7 build. Those older-Java builds never got SCRAM at any version. That catches people: a 42.2.x.jre7 jar looks newer than 42.2.1, and it still can't log in.
The same change hits other clients. The floors I check against, each taken from the client's own changelog or source:
- Go:
lib/pq1.1.0,pgx3.4.0 - Node:
pg7.9.0 - .NET: Npgsql 3.2.7
- Anything built on libpq: libpq 10
- PgBouncer: 1.11, or 1.14 if it looks passwords up with
auth_queryor keeps SCRAM secrets inauth_file
How to check before you upgrade (all read-only)
1. Find the PostgreSQL 14 servers in your cluster.
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{" "}{end}{"\n"}{end}' \ | grep -E 'postgres[^ ]*:14' If an operator (CloudNativePG, Crunchy, Zalando) runs the database, check the image in its cluster resource as well.
2. See how each login role's password is stored today. As a superuser:
SHOW password_encryption; SELECT rolname, CASE WHEN rolpassword LIKE 'md5%' THEN 'md5' WHEN rolpassword LIKE 'SCRAM-SHA-256%' THEN 'scram' ELSE 'none' END AS stored_as FROM pg_authid WHERE rolcanlogin; Every md5 row is a password that will turn into SCRAM the next time someone resets it.
3. Find the driver each Java service actually ships. Not the version in a pom from two years ago, the jar in the running image:
kubectl exec -n <ns> <pod> -- sh -c 'find / -name "postgresql-*.jar" 2>/dev/null' Anything below 42.2.1, or with .jre6/.jre7 in the name, gets bumped before the upgrade, not after the first failed rotation.
Why this one is worth an hour this week
The end-of-life date is a deadline you can see. This failure is one you can't: the upgrade passes, the dashboards stay green, and it surfaces on whatever day a password rotates. Usually not a day anyone connects with the upgrade.
Checking takes three queries. Finding it during an incident takes a lot longer.

