Transaction ID Wraparound
PostgreSQL reuses 32-bit transaction IDs. Vacuum must freeze old rows before the cluster approaches wraparound shutdown on PostgreSQL 18.4.
Search across all documentation pages
PostgreSQL reuses 32-bit transaction IDs. Vacuum must freeze old rows before the cluster approaches wraparound shutdown on PostgreSQL 18.4.
Quick-reference recipe card - copy-paste ready.
SELECT datname, age(datfrozenxid) AS frozen_age, datfrozenxid
FROM pg_database
ORDER BY age(datfrozenxid) DESC;When to reach for this: Logs mention wraparound, autovacuum runs with autovacuum (to prevent wraparound), or age(datfrozenxid) exceeds hundreds of millions.
CREATE TABLE ledger (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
amount numeric(12,2) NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO ledger (amount)
SELECT (random() * 100)::numeric(12,2) FROM generate_series(1, 100000);
-- Churn creates tuples needing freeze
UPDATE ledger SET amount = amount + 1;
SELECT
c.relname,
age(c.relfrozenxid) AS table_frozen_age,
s.n_dead_tup,
s.last_autovacuum
FROM pg_class c
JOIN pg_stat_user_tables s ON s.relid = c.oid
WHERE c.relname = 'ledger';
SELECT datname, age(datfrozenxid) AS db_frozen_age
FROM pg_database
WHERE datname = current_database();What this demonstrates:
age(relfrozenxid) per tabledatfrozenxid ageautovacuum_freeze_max_age (default 200M) forces aggressive vacuum.| Metric | Action level |
|---|---|
age(datfrozenxid) > 400M | Investigate vacuum lag |
age(datfrozenxid) > 600M | Urgent tuning |
age(datfrozenxid) > 800M | Incident response |
SELECT relname, last_autovacuum, last_vacuum
FROM pg_stat_user_tables
ORDER BY age(relid) DESC NULLS LAST
LIMIT 15;
VACUUM FREEZE VERBOSE ledger;idle_in_transaction_session_timeout, monitor pg_stat_activity.xact_start.pg_replication_slots.VACUUM FREEZE.age(relfrozenxid) dashboards.| Alternative | Use When | Don't Use When |
|---|---|---|
Lower autovacuum_freeze_max_age per table | Huge static archive | Default OLTP |
| Partition detach/drop | Old data disposable | Legal retention required |
| Read-only archive cluster | Split cold data | Single DB simplicity |
Forces vacuum to freeze pages when table relfrozenxid age exceeds setting.
Freeze vacuum prioritizes xmin advancement; may run with urgency flag.
Separate horizon; monitor pg_database.datminmxid age similarly.
Can hold xmin; prevent vacuum if lagging consumer.
Watch primary ages; standbys do not advance frozenxid locally for user data.
Replication setting delaying cleanup; lower on modern streaming unless needed.
Emergency vacuum or manual VACUUM FREEZE all tables; kill blockers.
Safety net for static tables with rare autovacuum triggers.
Combine pg_stat_user_tables with custom alerts on age thresholds.
Table and index bloat when vacuum lags but before wraparound emergency.
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.
Reviewed by Chris St. John·Last updated Jul 18, 2026