On 12 November 2026, PostgreSQL 14 gets its final release. After that date, no more fixes, not even for a security flaw. One week later, PostgreSQL 19 is due to ship as a final release.
Prepare your upgrade before support ends. Before you pick 18 or 19, ask yourself a simple question. Do your dumps restore on the new version?
TL;DR: I backed up the real schema of one of my projects on PostgreSQL 14. Then I restored it on 18 and on 19 RC1. The data comes through unchanged. But without the roles, application privileges disappear, and psql still reports success. And when my 14 server had
standard_conforming_stringsset tooff, 19 left a table empty. Test your restores on the target version, and take your dumps with itspg_dump.
Versions and source status: snapshot as of October 31, 2026. Check the latest fixes before deploying.
This article is for developers and teams running PostgreSQL 14 in production. It also helps if you keep PostgreSQL 14 dumps in an archive. No deep PostgreSQL knowledge needed.
The key lab result is simple: data can come back while permissions are lost. psql can even return 0. Check errors, data and permissions together.
The calendar: 14 stops, 19 arrives
PostgreSQL supports each major version for five years. The major version is the first number: 14, 18 or 19. Minor versions, like 14.24, only bring fixes.
Version 14 came out on 30 September 2021. The official table sets its final release on 12 November 2026. After that, a flaw found in 14 will not be fixed.
Version 18 is the current stable release, at 18.6. It is supported until 14 November 2030. Version 19 has been a release candidate since 15 October. Its final release is planned for 19 November, unless a problem delays it.
To move from one major version to another, you need a dump and a restore, or the pg_upgrade tool. The documentation explains why. The internal data format can change between two major versions.
A physical backup copies exactly those internal files. It gives you back a 14, not an 18. To change versions from a backup, you need a dump.
My test: a real schema, three PostgreSQL versions
PostgreSQL is in the stack of every one of my freelance projects. So before writing about this date, I wanted to test it for real.
I took the schema of one of my projects: 11 databases, one per service, and 129 SQL migrations. That makes 58 tables, triggers, functions and 72 RLS policies. I described them in my article on tenant isolation.
I added a few made-up rows, with no real data at all. Some of them hold traps: backslashes (the \ character), apostrophes, emoji and JSON.
Then I exported each database with pg_dump 14.24, in two formats:
- the plain format, an SQL script you replay with
psql; - the custom format, a binary file you read back with
pg_restore.
I restored each dump on PostgreSQL 18.6 and on 19 RC1, with the tools of each version. Everything ran in Docker, with the official images.
To compare, I computed a fingerprint per table: the row count and a hash of the content. I also counted indexes, constraints, policies and privileges. Same fingerprint, same data.
Good news: the data comes through unchanged
With the default settings, my 14 dumps restore on 18 and on 19. All 58 tables keep the same fingerprint as the source.
Indexes, triggers, functions and RLS policies are there too. That holds for both formats and both versions.
But I had recreated the roles on the target server before restoring. Without them, the result is very different.
The data comes back, the privileges do not, and psql says success. Three restores of my 14 schema on 18 and 19. Without the roles, 393 privileges disappear. With an old setting, 19 leaves a table empty.
Trap 1: your dump does not contain your roles
A role is a PostgreSQL account. Your services connect with it, and table privileges are granted to these roles.
pg_dump exports a single database. Roles belong to the whole server, so they are not in the dump. The documentation points to pg_dumpall to export them.
My schema grants privileges to 24 application roles. I restored the dumps on a fresh server, without those roles. psql printed this kind of line 126 times:
ERROR: role "app_identity_app" does not exist All the data is there. But three things changed, on 18 as on 19:
- The 393 table privileges granted to the application roles are gone.
- 5 of the 72 RLS policies no longer exist. They targeted a specific role, so creating them failed.
- In custom format, two functions restricted to my application roles became executable by any role again.
The last point is the sneakiest. In a custom dump, revoking the public privilege and granting the role's privilege travel in the same command. When the grant fails, the revoke is rolled back with it.
And psql? On a single database, it printed 35 errors, then returned exit code 0, the success code. A CI job reading that code would show green. pg_restore returns 1.
The fix takes two steps. Export the roles separately, then recreate them on the target server before the databases:
# On the source: roles and tablespaces, including password hashes. pg_dumpall -U postgres --globals-only > globals.sql # On the target: review duplicate CREATE ROLE statements first. psql -X -v ON_ERROR_STOP=1 -U postgres -f globals.reviewed.sql In my trial, one error came from the postgres role already present on the new server. Prepare a copy named globals.reviewed.sql before a strict restore. Check existing roles. Remove only reviewed duplicate CREATE ROLE statements. Keep the intended attributes and memberships. Any other error must stop the restore.
This file holds the password hashes of your roles. Store it like a secret.
Also add -v ON_ERROR_STOP=1 to psql, or --exit-on-error to pg_restore. The first error then stops the restore, with a failure exit code.
Trap 2: an old setting that 19 refuses
standard_conforming_strings decides how PostgreSQL reads a backslash in a string. With on, a backslash is a normal character. With off, it escapes the next character.
The default has been on since PostgreSQL 9.1. But if your server has it off, every dump records it in its first lines:
SET standard_conforming_strings = off; The PostgreSQL 19 release notes are clear. This setting is now forced to on. A dump made by an older pg_dump, on a server set to off, will not load properly.
I wanted to see what "not properly" means. I switched my 14 server to off, then dumped three databases again. On 18, everything restores without an error. On 19, the restore prints these errors at the start, then carries on:
ERROR: non-standard string literals are not supported ERROR: unrecognized configuration parameter "escape_string_warning" Further down, one of my tables stays empty. It has a CHECK constraint that validates emails with a regular expression:
CHECK (email ~* '^[^@\s]+@[^@\s]+\.[^@\s]+$') On a server set to off, pg_dump writes the backslashes of this constraint twice. Version 19 now reads those two backslashes as two characters. The regular expression changes meaning, and my test emails no longer pass. Loading the whole table fails.
With the --inserts option of pg_dump, it gets worse. Rows are written as INSERT statements, and every backslash comes back doubled in your data. No error reports it:
on 14: Le fichier est dans C:\Partage\Docs on 19: Le fichier est dans C:\\Partage\\Docs Here again, psql returned exit code 0.
I tested two fixes, and both work. If your 14 server is still running, take the dump with the pg_dump of 19. It writes SET standard_conforming_strings = on, and the restore is clean.
If all you have left is the file, restore it on an 18 first. Then take a new dump from that 18, with the pg_dump of 19. My values came back intact.
To find out whether your dumps are affected, one line is enough:
# plain format grep -m1 standard_conforming_strings base.sql # custom format pg_restore -f - base.dump | grep -m1 standard_conforming_strings Take your dumps with the pg_dump of the target version
In the other direction, everything went fine. The pg_dump of 18 and of 19 read my 14 server without a single warning. Their dumps restored without an error, with the same fingerprints.
It is also the official advice. For a major upgrade, use pg_dump and pg_dumpall from the new version. With Docker, you do not even need to install them:
docker run --rm -e PGPASSWORD postgres:18.6 \ pg_dump -h old-server -U postgres -Fc -d my_db > my_db.dump Plan the rollback before you migrate
I also tested the reverse path. The pg_restore of 14 refuses a custom dump made by the pg_dump of 18 or 19:
pg_restore: error: unsupported version (1.16) in file header And the pg_dump of 14 refuses to read a 19 server:
pg_dump: error: aborting because of server version mismatch The documentation confirms it. Nothing guarantees that a dump loads into an older major version. So keep the old server and its tools as long as a rollback is still possible. Keep a dump made with those tools too.
The checklist
A backup counts if it restores on the version where you will use it.
- [ ] List your PostgreSQL 14 servers and their migration date
- [ ] Run
SHOW standard_conforming_strings;on each one - [ ] Look for that line in the dumps you keep in your archive
- [ ] Export the roles with
pg_dumpall --globals-only, and store the file like a secret - [ ] Restore a real dump on the target version, in a throwaway container
- [ ] Add
-v ON_ERROR_STOP=1to psql and--exit-on-errorto pg_restore - [ ] Compare rows, privileges and RLS policies before and after
- [ ] Take the migration dumps with the
pg_dumpof the target version - [ ] Keep the old server and its tools until a rollback is no longer possible
What to remember
In my article on backups, I wrote that a backup never restored is only a hope. A major upgrade adds a question: restored on which version?
In my test, with the default settings, the data from 14 came through unchanged. What broke came from the side: roles missing from the dump, and an old server setting. And psql returned the success code in both cases.
You need to migrate a PostgreSQL 14 and want to test your restores before 12 November? Let's talk.
Sources: PostgreSQL, versioning policy · PostgreSQL 19 RC 1, announcement of 15 October 2026 · PostgreSQL 19 release notes · PostgreSQL, upgrading a cluster · pg_dump · pg_dumpall · pg_restore · psql, exit status · standard_conforming_strings · Official postgres Docker images
