Upgrade PostgreSQL 15 → 18
We currently run quay.io/sclorg/postgresql-15-c9s (dev compose) and PG15 in production.
PG15 goes EOL November 2027, so a major upgrade is due regardless. SCLorg skipped 17
but publishes actively-maintained 18 images (postgresql-18-c9s / postgresql-18-c10s),
and PG18 (released September 2025) is now a year old and several minor releases in.
Jumping straight to 18 gets us everything from 16 and 17 on the way and is supported
until November 2030.
Stack compatibility is already in place: psycopg 3.3.x has explicit PG18 support, and
Django 5.2 works against 18 (official support is declared in Django 6.0, which we're
heading toward anyway).
Benefits
Inherited from PostgreSQL 16
Inherited from PostgreSQL 17
New in PostgreSQL 18
Caveats / gotchas
Tasks
Upgrade PostgreSQL 15 → 18
We currently run
quay.io/sclorg/postgresql-15-c9s(dev compose) and PG15 in production.PG15 goes EOL November 2027, so a major upgrade is due regardless. SCLorg skipped 17
but publishes actively-maintained 18 images (
postgresql-18-c9s/postgresql-18-c10s),and PG18 (released September 2025) is now a year old and several minor releases in.
Jumping straight to 18 gets us everything from 16 and 17 on the way and is supported
until November 2030.
Stack compatibility is already in place: psycopg 3.3.x has explicit PG18 support, and
Django 5.2 works against 18 (official support is declared in Django 6.0, which we're
heading toward anyway).
Benefits
Inherited from PostgreSQL 16
pg_stat_ioview — proper per-backend-type I/O observabilityCOPY(bulk loading) and expanded SIMD acceleration (JSON/ASCII processing)Inherited from PostgreSQL 17
WHERE id IN (...)/= ANY(...)executes as asingle index scan instead of one descent per value. The ORM and our RBAC filtering
generate large
INlists constantly; this is the biggest free win for API list-view latency.1GB cap removed, fewer index passes. Helps the tables that churn in place
(
main_unifiedjob,main_host, sessions); event partitions already dodge vacuum bybeing dropped whole.
under high concurrency. Directly relevant to concurrent bulk inserts from
callback-receiver workers (the job-event firehose).
partition DDL if we ever align it with Django 5's native PK handling)
pg_basebackup --incremental)ANALYZEvia streaming I/ONew in PostgreSQL 18
bitmap heap scans, vacuum). Default
io_method = workerworks in any container.leading column; rescues ORM queries that today fall back to seq scans
pg_upgradepreserves planner statistics — coming from 15, we skip the traditionalpost-upgrade window of bad plans until a full
ANALYZEcompletes (painful with largeevent tables). Also
--swapmode and better parallelism for a shorter cutover.uuidv7()— timestamp-ordered, index-friendly UUIDs for future schema workEXPLAINshows buffer usage by default, improvedper-connection I/O stats
Caveats / gotchas
pg_upgraderequires old and newclusters to match, and our PG15 clusters almost certainly have them off. The new cluster
must be initialized with
--no-data-checksums(or convert the old one first withpg_checksums). Affects production upgrade tooling; dev compose just starts a fresh volume.io_method = io_uringin shared config templates. Postgres fails atstartup (no fallback) where io_uring is unavailable — and it is unavailable on our
infrastructure by default:
kernel.io_uring_disabled = 2(disabled kernel-wide)pods set
seccompProfile: RuntimeDefaultor the restricted Pod Security Standard applies)io_method = worker; it captures most of the AIO benefit.io_uring is an optional later opt-in for dedicated single-tenant DB nodes only
(node sysctl + seccomp exception).
SCRAM already, but grep any
pg_hbatemplates in the installer to confirm.with psycopg 3.3, but run the full test suite against 18 before cutover.
pg_dump/pg_restorein setup playbooks and the operator) mustuse PG18 client binaries against the 18 server.
adminpackextension was removed (in 17) — only relevant if a deployment tool installed it.pg_upgradegoes 15 → 18 directly; no need to step through intermediate majors.Tasks
quay.io/sclorg/postgresql-18-c9sand smoke-test the stack