S12: a Keycloak *minor* upgrade raised the minimum Postgres major
- Upgrade
- 26.0.0 → 26.7.1
- Scale
- 202 users, 2 realms (fresh install + seed per major)
- Database
- PostgreSQL 13.23 / 12.22 / 11.16, single node, container — the variable
- Topology
- single container
- Host
- Hetzner CCX33 (fsn1) — 8 vCPU dedicated, 30 GB RAM, local NVMe
- Condition
- Older Postgres major
Summary
Keycloak 26.0.0 runs on PostgreSQL 12. Keycloak 26.7.1 refuses to. The minimum supported Postgres major moved from 12 to 13 inside major version 26 — across what looks, from the version number, like a routine minor bump.
ERROR: Persistence unit 'keycloak-default' was configured to run with a database
version of at least '13.0.0', but the actual version is '12.22.0'.
The upgrade fails before the migration. Nothing is applied, no data is lost, and the fix is a database upgrade — which is a different project, a different change window and usually a different team.
Environment
| Field | Value |
|---|---|
| Keycloak from → to | 26.0.0 → 26.7.1 |
| Distribution / start mode | quay.io/keycloak/keycloak official image, start w/ external DB |
| Database | PostgreSQL 13.23 / 12.22 / 11.16, single node, container — the variable |
| Postgres tuning | stock defaults for each major |
| Adverse scenario | S12 |
| Dataset scale | 202 users, 2 realms (fresh install + seed per major) |
| Seeding method | partialImport (bin/seed-realm.sh --profile typical --users 200) |
| Topology | single container |
| Host | Hetzner CCX33 (fsn1) — 8 vCPU dedicated, 30 GB RAM, local NVMe |
| JVM heap | -Xms1g -Xmx4g |
Each major started from an empty pgdata volume: fresh install at 26.0.0, seed,
then the upgrade under test.
Results
| Postgres | Keycloak 26.0.0 | Keycloak 26.7.1 |
|---|---|---|
| 13.23 | installs, ready 19s | upgrade READY in 16s — model 26.7.1, changelog 211, 202 users, login 200 |
| 12.22 | installs, ready 19s, changelog 144, 202 users seeded | REFUSES TO START — requires ≥ 13.0.0 |
| 11.16 | REFUSES TO START — requires ≥ 12.0.0 | not reached |
So the floors are:
| Keycloak | Minimum Postgres major |
|---|---|
| 26.0.0 | 12 |
| 26.7.1 | 13 |
The failure is clean
After 26.7.1 refused to start on Postgres 12.22:
migration_model | 26.0.0 — unchanged |
DATABASECHANGELOG | 144 — unchanged |
| Users | 202 — intact |
| Container | exited 1 |
Fail-closed, like S3, S4 and S5 before it. The database is exactly as it was and rolling back means putting the old image tag back.
The escape hatch, and its wording
Both versions offer one, and they do not name the same property:
| Refusing version | Suggested override |
|---|---|
| 26.7.1 on PG 12 | jakarta.persistence.database-product-version=12.22.0 |
| 26.0.0 on PG 11 | quarkus.datasource.db-version=11.16.0 |
Both append the same warning: "but this may disable some features and/or impact performance negatively". We did not test either override. It is a documented way to start on an unsupported database; it is not a supported configuration, and a rescue engagement should treat it as a bridge to a database upgrade rather than a destination.
Verification
| Claim | Primary source | Checked |
|---|---|---|
| 26.7.1 upgrades cleanly on PG 13.23 | run: READY 16s, model 26.7.1, changelog 211, login 200 | ☑ |
| 26.0.0 installs and serves on PG 12.22 | run: ready 19s, changelog 144, 202 users seeded | ☑ |
| 26.7.1 refuses on PG 12.22 | at least '13.0.0', but the actual version is '12.22.0' | ☑ |
| 26.0.0 refuses on PG 11.16 | at least '12.0.0', but the actual version is '11.16.0' | ☑ |
| The refusal applies nothing | changelog 144, model 26.0.0, 202 users after the failure | ☑ |
| Which 26.x release moved the floor | not bracketed — only known to be in (26.0.0, 26.7.1] | ☐ |
| Whether the override flags work | not tested — deliberately | ☐ |
| Whether PG 13 is the floor for later Keycloak | not tested — the floor evidently moves, so this expires | ☐ |
| Managed-service equivalents (RDS/Cloud SQL/Azure majors) | not tested — same engine versions, different upgrade mechanics | ☐ |
Publishable extract
Keycloak 26.0.0 runs on PostgreSQL 12. Keycloak 26.7.1 does not. Same major version of Keycloak.
We installed Keycloak 26.0.0 on Postgres 12.22, seeded a realm, and upgraded to 26.7.1 — the kind of bump most change advisory boards would wave through as a minor. It stopped before it started:
ERROR: Persistence unit 'keycloak-default' was configured to run with a database version of at least '13.0.0', but the actual version is '12.22.0'.The floors we measured:
Keycloak Minimum Postgres 26.0.0 12 26.7.1 13 On Postgres 13.23 the same upgrade took sixteen seconds and worked perfectly.
The good news is that the refusal is clean: 144 changelog rows before, 144 after, all 202 users present, container exited 1. Nothing was half-done. The bad news is what the fix is. You cannot resolve this inside the Keycloak change window — you need a database major upgrade, which is a different project with a different risk profile and usually a different team. If you discovered it during the window, the window is over.
Keycloak does offer an override (
jakarta.persistence.database-product-version) and warns in the same breath that it "may disable some features and/or impact performance negatively". We deliberately did not test it. Treat it as a bridge to a database upgrade, not a destination.The pre-flight question is one line, and almost nobody asks it, because the Keycloak version number does not suggest it needs asking:
select version();