Extension Upgrade Path
Extension versions move independently from Postgres majors. Plan OS package upgrades, ALTER EXTENSION UPDATE, and regression tests as a single change window, especially for pgvector and PostGIS.
Recipe
-- Check current vs target version
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'vector';
SELECT name, default_version
FROM pg_available_extensions
WHERE name = 'vector';
-- Apply available upgrade scripts
ALTER EXTENSION vector UPDATE TO '0.8.0';
-- Verify
SELECT extname, extversion FROM pg_extension WHERE extname = 'vector';When to reach for this: After patching Postgres, deploying new extension packages, or before building HNSW indexes that require a minimum pgvector version.
Working Example
-- Safe upgrade runbook (run on staging first)
BEGIN;
-- 1) Snapshot inventory
CREATE TEMP TABLE ext_before AS
SELECT extname, extversion FROM pg_extension;
-- 2) Upgrade one extension
ALTER EXTENSION postgis UPDATE;
-- 3) Compare
SELECT b.extname,
b.extversion AS before_version,
e.extversion AS after_version
FROM ext_before b
JOIN pg_extension e ON e.extname = b.extname
WHERE b.extversion IS DISTINCT FROM e.extversion;
-- 4) Smoke test
SELECT PostGIS_Version();
SELECT ST_Distance(
ST_MakePoint(-73.9857, 40.7484)::geography,
ST_MakePoint(-118.2437, 34.0522)::geography
) AS nyc_to_la_meters;
COMMIT;What this demonstrates:
- Inventory before and after prevents silent skips
ALTER EXTENSION UPDATEwithoutTOapplies latest available script- Functional smoke queries catch broken SONAME mismatches early
- Transaction wraps metadata change; OS libs must already match
Deep Dive
Upgrade Mechanics
- OS / package layer - Install
postgresql-18-postgis-3.5(or vendor equivalent) on every node. - Database layer -
ALTER EXTENSION name UPDATE [TO 'version']runs SQL migration scripts inEXTENSION(name)/. - Index layer - Some upgrades require
REINDEX(pgvector index opclass changes). Read release notes.
-- List upgrade path scripts shipped with the extension
SELECT version, relocatable
FROM pg_available_extension_versions
WHERE name = 'vector'
ORDER BY version;Minor vs Major Extension Versions
| Scenario | Postgres major | Extension action |
|---|---|---|
| pgvector 0.7 to 0.8 | Same PG 18 | ALTER EXTENSION UPDATE, possible reindex |
| PostGIS 3.4 to 3.5 | Same PG 18 | ALTER EXTENSION UPDATE, run postgis_extensions_upgrade() if docs say so |
| PG 17 to PG 18 | Major bump | pg_upgrade or dump/restore, reinstall packages, then ALTER EXTENSION UPDATE |
Multi-Node Clusters
- Primary first - Apply packages and
ALTER EXTENSIONon primary. - Replicas - Packages must exist before replication replays extension-dependent queries.
- Patroni - Rolling OS package sync, then controlled
ALTER EXTENSIONduring maintenance window. - Managed cloud - Vendor bumps extension catalog; you still run
ALTER EXTENSIONin each database.
Rollback Reality
There is no ALTER EXTENSION DOWNGRADE. Rollback means restore from backup or recreate indexes after reinstalling older packages (high risk). Test upgrades on a snapshot clone.
Gotchas
- Binary/SQL mismatch - New SQL script with old
.sofile causes runtime crashes. Fix: Upgrade packages on all nodes beforeALTER EXTENSION. - Long-running reindex - pgvector HNSW rebuild blocks or consumes I/O. Fix:
REINDEX CONCURRENTLYwhere supported; schedule off-peak. - Forgotten databases - Extension updated in
appdbbut notanalytics. Fix: Loop all databases:\l+ per-DBALTER EXTENSION. - Breaking opclass names - Client queries reference old operator classes. Fix: Search codebase for index definitions; migrate in expand-contract phases.
- PostGIS topology extras - Separate extensions (
postgis_topology) need their ownUPDATE. Fix: Inventory allpostgis*rows inpg_extension. - Logical replication drift - Subscriber missing extension version. Fix: Match extension versions before subscribing to DDL-heavy publications.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Blue/green database | Zero-downtime cutover required | Extension change is minor and tested |
| Dump/restore to new cluster | Major PG + extension jump together | Database size makes dump impractical |
| Freeze extension version | Stability over features | Security patch requires minimum version |
| Side-by-side new DB | Rebuild indexes from scratch is faster than in-place upgrade | App cannot tolerate dual-write period |
FAQs
ALTER EXTENSION UPDATE vs CREATE EXTENSION?
UPDATE migrates an installed extension. CREATE is first install only.Can I pin TO a specific version?
Yes:ALTER EXTENSION vector UPDATE TO '0.8.0'; if that script exists in pg_available_extension_versions.What if UPDATE says already latest?
Installed version matches highest available script. Check OS packages if you expected newer.Does UPDATE lock tables?
Metadata locks are brief. Follow-up REINDEX or VALIDATE CONSTRAINT can lock longer.pg_upgrade and extensions?
pg_upgrade preserves pg_extension rows. Install new major packages and run UPDATE after upgrade.How to automate?
Migration tools (Flyway/Liquibase) can shipALTER EXTENSION in versioned SQL after Ansible package sync.Extension in template databases?
Upgrade template1 and postgres DB too, or new databases inherit old versions.Cloud SQL extension updates?
Google/AWS enable versions per instance generation. Read vendor matrix before planning.Verify index health post-upgrade?
SELECT indexrelid::regclass, indisvalid FROM pg_index WHERE NOT indisvalid;TimescaleDB upgrades?
Follow Timescale release notes; may require toolkit extension ordering separate from core postgresql.Related
- Extensions Basics - install and preload
- Trusted vs Untrusted Extensions - security review
- IVFFlat vs HNSW Indexes - index rebuild implications
- Flyway and Liquibase - migration automation
- Extensions Best Practices - pin versions in IaC
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PostGIS 3.5+, pgbouncer 1.x, and Patroni 3.x.