Reference
Scope
Look-up facts for PostgreSQL: the supported-version and EOL matrix, minor release history, PostgreSQL 18 and 19 feature and compatibility tables, configuration parameter defaults, hard limits, authentication and privilege tables, a hardening checklist, ecosystem versions, and rough performance figures. Tasks are in How-to Guides. Internals and design reasoning are in Explanation.
Version and Support Matrix
The PostgreSQL Global Development Group (PGDG) supports each major version for 5 years after its first release. It then ships one final minor release, and the version is end-of-life (EOL). Data as of 2026-09-25.
| Major |
First release |
Latest minor (date) |
Final release / EOL |
Status (2026-09-25) |
| 19 |
TBD (GA targeted for October 2026) |
19 Beta 4 (2026-09-24) |
TBD (the 5-year policy puts it around November 2031) |
Beta, not for production |
| 18 |
2025-09-25 |
18.6 (2026-08-13) |
2030-11-14 |
Supported, current |
| 17 |
2024-09-26 |
17.11 (2026-08-13) |
2029-11-08 |
Supported |
| 16 |
2023-09-14 |
16.15 (2026-08-13) |
2028-11-09 |
Supported |
| 15 |
2022-10-13 |
15.19 (2026-08-13) |
2027-11-11 |
Supported |
| 14 |
2021-09-30 |
14.24 (2026-08-13) |
2026-11-12 |
Supported, final minor release due 2026-11-12 |
| 13 |
2020-09-24 |
13.23 (2025-11-13) |
2025-11-13 |
EOL |
| 12 |
2019-10-03 |
12.22 |
2024-11-21 |
EOL |
PostgreSQL 14 reaches EOL on 2026-11-12
The November 2026 minor release is the last one for 14.x. Plan major upgrades to 17 or 18. Once 19 is GA and has had a few minor releases, 19 is also an option.
Release Cadence
| Item |
Rule |
| Major releases |
One per year, usually in September or October |
| Support window |
5 years per major version, then one final minor release |
| Minor releases |
Second Thursday of February, May, August and November. Out-of-cycle releases happen for serious regressions or security issues |
| Next scheduled minor release |
2026-11-12 (expected 18.7, 17.12, 16.16, 15.20, and the final 14.25) |
| Upgrade within a major |
Replace binaries and restart. No dump/restore needed, but always read the release notes' "Migration" section |
| Upgrade across majors |
pg_upgrade, dump/restore, or logical replication |
PostgreSQL 18 Minor Releases
| Version |
Date |
Notes |
| 18.0 |
2025-09-25 |
Major release (AIO, skip scan, uuidv7(), OAuth, virtual generated columns) |
| 18.1 |
2025-11-13 |
Regular quarterly release. Also the final 13.x release (13.23) |
| 18.2 |
2026-02-12 |
Quarterly release. Fixes CVE-2026-2003 to CVE-2026-2007 (CVE-2026-2006: missing multibyte length validation that allows arbitrary code execution) |
| 18.3 |
2026-02-26 |
Out-of-cycle release. It fixes regressions in 18.2, for example WAL replay of multixid truncation records on standbys |
| 18.4 |
2026-05-14 |
Quarterly release. Fixes 11 CVEs (CVE-2026-6472 to 6479, 6575, 6637, 6638) |
| 18.5 |
never released |
Withdrawn because of a regression found after the release was wrapped |
| 18.6 |
2026-08-13 |
Fixes 28 security issues and more than 110 bugs. Adds the output_plugin_libraries allow-list for logical decoding plugins (CVE-2026-6471) and fixes the pgcrypto PGP cipher-failure bug (CVE-2026-14663). If you use GIN, btree_gist or ltree indexes, read the post-update steps |
18.6 post-update actions
- Logical decoding plugins other than
pgoutput and test_decoding must be added to output_plugin_libraries, or they stop working. pg_upgrade --check fails if the new cluster does not allow the plugins that the old cluster's slots use.
- Before 18.6, pgcrypto PGP encryption with a cipher that OpenSSL rejected (FIPS mode, or legacy ciphers such as
bf, cast5, 3des) silently produced data that was only XOR-wrapped, not encrypted. Decrypting that data now fails unless you pass ignore-cipher-failure=1. Find the affected data and re-encrypt it with a modern cipher.
- Some indexes built with
btree_gist or ltree may need a REINDEX. GIN-indexed tables may have corrupt reltuples values. See the 18.6 release notes.
PostgreSQL 18 Feature Reference
| Feature |
What it does |
Controls / syntax |
| Asynchronous I/O (AIO) |
Backends queue several reads at once. Used by sequential scans, bitmap heap scans and VACUUM |
io_method = worker (default), io_uring (Linux, needs a --with-liburing build) or sync. Also io_workers (default 3), io_combine_limit / io_max_combine_limit (default 128kB), view pg_aios |
| B-tree skip scan |
A multicolumn B-tree index can be used even when the leading column has no condition (or only a non-equality one) |
Automatic, chosen by the planner |
uuidv7() |
Generates timestamp-ordered UUIDs. uuidv4() is added as an explicit alias for random UUIDs |
DEFAULT uuidv7() |
| Virtual generated columns |
The value is computed when the column is read. Virtual is now the default kind of generated column |
GENERATED ALWAYS AS (expr) VIRTUAL. Use STORED for the old behavior |
OLD/NEW in RETURNING |
INSERT/UPDATE/DELETE/MERGE can return values from before and after the change |
RETURNING old.price, new.price |
| Temporal constraints |
Primary key, unique and foreign key constraints over ranges |
PRIMARY KEY (id, valid_at WITHOUT OVERLAPS), FOREIGN KEY (..., PERIOD valid_at) |
| OAuth 2.0 authentication |
New oauth method in pg_hba.conf. Bearer tokens are checked by a validator library that you load. Core ships no validator |
oauth_validator_libraries. libpq must be built --with-libcurl |
pg_upgrade keeps planner statistics |
No full ANALYZE is needed right after an upgrade. Extended statistics are not kept |
--no-statistics turns it off. New --swap mode and parallel checks with --jobs |
| Data checksums on by default |
initdb enables page checksums |
initdb --no-data-checksums. pg_upgrade needs both clusters to have the same checksum setting |
| Parallel GIN index builds |
CREATE INDEX ... USING gin can use parallel workers |
max_parallel_maintenance_workers |
| Eager freezing |
Normal VACUUM freezes some all-visible pages early, so later aggressive vacuums have less work |
vacuum_max_eager_freeze_failure_rate |
| Autovacuum tuning |
A fixed upper threshold for dead tuples, and runtime-adjustable worker counts |
autovacuum_vacuum_max_threshold (default 100,000,000), autovacuum_worker_slots |
| Idle slot invalidation |
Replication slots that stay inactive too long are invalidated |
idle_replication_slot_timeout (default 0, disabled) |
| Logical replication |
Generated column values can be replicated. Subscription streaming now defaults to parallel. Apply conflicts are logged and counted |
publish_generated_columns, pg_stat_subscription_stats |
| Wire protocol 3.2 |
First protocol version bump since 7.4. Allows 256-bit cancel keys |
libpq min_protocol_version / max_protocol_version |
| TLS |
TLS 1.3 cipher suite control and X25519 in the default groups |
ssl_tls13_ciphers, ssl_groups (the renamed ssl_ecdh_curve) |
| I/O concurrency defaults |
effective_io_concurrency and maintenance_io_concurrency defaults raised from 1/10 to 16 |
— |
| Monitoring |
pg_stat_io reports bytes and WAL rows. Per-backend I/O and WAL statistics. Vacuum and analyze timing columns in pg_stat_all_tables |
pg_stat_get_backend_io(), track_cost_delay_timing |
| pgcrypto |
crypt() supports sha256crypt and sha512crypt |
gen_salt('sha512crypt') |
PostgreSQL 18 Compatibility Changes
| Change |
Impact |
initdb enables data checksums by default |
pg_upgrade from a cluster without checksums needs initdb --no-data-checksums on the new cluster |
| MD5 password authentication deprecated |
CREATE/ALTER ROLE ... PASSWORD warns when it stores an MD5 hash (md5_password_warnings). Removal is planned for a future major version |
VACUUM/ANALYZE process inheritance children |
Use ONLY to get the old behavior |
COPY FROM CSV no longer treats \. as end-of-file |
Older psql \copy clients may have problems |
| Unlogged partitioned tables are disallowed |
— |
AFTER triggers run as the role that was active when the event was queued |
Matters when the role changes before commit |
| Full-text search uses the cluster's default collation provider |
Reindex full-text and pg_trgm indexes after pg_upgrade on ICU or builtin-provider clusters |
PostgreSQL 19 (Beta) Reference
PostgreSQL 19 Beta 1 shipped on 2026-06-04, Beta 2 on 2026-07-16, Beta 3 on 2026-08-13, and Beta 4 on 2026-09-24. The release team plans a release candidate in early October and GA by the end of October 2026. As of 2026-09-25, RC and GA dates are not formally announced.
| Headline feature (per the 19 release notes as of 2026-09-14) |
Notes |
REPACK command |
Replaces VACUUM FULL and CLUSTER, which are kept for compatibility. REPACK (CONCURRENTLY) rebuilds a table without blocking reads and writes |
pg_plan_advice / pg_stash_advice modules |
Control and stabilize planner decisions, and apply plan advice automatically |
| Auto-scaling I/O workers |
io_min_workers, io_max_workers, io_worker_idle_timeout, io_worker_launch_interval |
| Logical replication without a restart |
With wal_level = replica, logical decoding is enabled automatically when it is needed |
| Sequence replication |
Logical replication of sequence values |
WAIT FOR LSN |
Read-your-writes on standbys: wait until an LSN is written, flushed or replayed |
| Parallel autovacuum |
autovacuum_max_parallel_workers, per-table autovacuum_parallel_workers, plus a scoring system (pg_stat_autovacuum_scores) |
| SQL additions |
INSERT ... ON CONFLICT DO SELECT, IGNORE NULLS for window functions, COPY TO ... (FORMAT json) |
| PostgreSQL 19 incompatibility |
Impact |
| RADIUS authentication removed |
Move to another method (LDAP, cert, OAuth) before upgrading |
standard_conforming_strings always on |
Dumps made by older pg_dump with the setting off will not load |
Default index opclasses for inet/cidr change from btree_gist's to core GiST |
The btree_gist inet/cidr opclasses can miss rows. pg_upgrade refuses clusters that have such indexes |
max_locks_per_transaction default 64 → 128 |
Lock sizing changed. Double custom values to keep the same capacity |
| JIT disabled by default |
Analytics-heavy sites must set jit = on explicitly |
default_toast_compression pglz → lz4 |
Also, wal_compression = on now picks the best available method |
| MD5 authentication warns after a successful login |
md5_password_warnings controls it |
MULE_INTERNAL encoding removed |
Dump and restore such databases into another encoding |
Features reverted during the 19 beta
Beta 4 (2026-09-24) reverted SQL/PGQ property graph queries, online data-checksum toggling and four other features. The PGDG news item has the full list. SQL/PGQ can land in PostgreSQL 20 at the earliest (expected around September 2027). Articles written during Beta 1–3 that list SQL/PGQ as a 19 feature are out of date.
Configuration Parameters
Defaults below are for PostgreSQL 18 and come from runtime-config in the 18 documentation. "Guidance" gives common starting points, not rules.
| Parameter |
Default (PG 18) |
Purpose |
Common guidance |
shared_buffers |
128MB |
Main buffer pool (8 KiB pages, clock-sweep eviction) |
About 25% of RAM on a dedicated host |
effective_cache_size |
4GB |
Planner hint for total cache (shared buffers plus OS page cache) |
About 50–75% of RAM |
work_mem |
4MB |
Memory per sort or hash node before spilling to disk |
Raise with care: it applies per node and per session |
maintenance_work_mem |
64MB |
Memory for VACUUM, CREATE INDEX and ALTER TABLE |
512MB–2GB on large hosts |
max_connections |
100 (typically) |
Maximum concurrent backends |
Keep it low and put a pooler in front |
wal_level |
replica |
How much WAL detail is written: minimal, replica or logical |
logical for logical decoding and CDC (19 can enable it on demand) |
max_wal_size |
1GB |
WAL volume that triggers a checkpoint |
4–16GB on write-heavy systems |
min_wal_size |
80MB |
WAL kept for recycling |
— |
checkpoint_timeout |
5min |
Maximum time between automatic checkpoints |
15–30min to reduce full-page-write volume |
checkpoint_completion_target |
0.9 |
Fraction of the interval over which checkpoint writes are spread |
Keep the default |
wal_buffers |
-1 (auto) |
WAL buffer size. Auto means 1/32 of shared_buffers, at least 64kB and at most one WAL segment (usually 16MB) |
Keep the default |
wal_compression |
off |
Compresses full-page images in WAL: pglz, lz4 or zstd |
lz4 or zstd |
max_wal_senders |
10 |
Maximum WAL sender processes |
Standbys plus backup tools plus headroom |
max_replication_slots |
10 |
Maximum replication slots |
— |
max_slot_wal_keep_size |
-1 (unlimited) |
Upper bound on the WAL a slot can retain |
Set it, so an abandoned slot cannot fill the disk |
idle_replication_slot_timeout |
0 (disabled) |
Invalidates slots that have been idle too long (new in 18) |
— |
autovacuum |
on |
Automatic VACUUM and ANALYZE |
Never disable it |
autovacuum_freeze_max_age |
200,000,000 |
XID age that forces an anti-wraparound vacuum |
— |
random_page_cost |
4.0 |
Planner cost of a random page read |
1.1–1.5 on SSD/NVMe |
effective_io_concurrency |
16 (was 1 before 18) |
Concurrent I/O hint for prefetching and AIO |
— |
io_method |
worker |
AIO implementation (new in 18). Requires a restart |
io_uring on Linux builds that include liburing |
io_workers |
3 |
I/O worker processes when io_method = worker |
Scale up for high I/O rates. PG 19 auto-scales them |
password_encryption |
scram-sha-256 (since 14) |
Hash format for new passwords |
Keep it |
ssl_min_protocol_version |
TLSv1.2 |
Minimum TLS version |
TLSv1.3 if every client supports it |
jit |
on (off by default in 19) |
LLVM JIT compilation of expressions |
Often turned off for OLTP |
default_toast_compression |
pglz (lz4 by default in 19) |
TOAST compression method |
lz4 |
Limits
These are hard limits from the PostgreSQL 18 documentation (Appendix K). Practical limits, such as performance or disk space, usually apply much earlier.
| Item |
Upper limit |
Comment |
| Database size |
Unlimited |
— |
| Number of databases |
4,294,950,911 |
— |
| Relations per database |
1,431,650,303 |
— |
| Relation (table) size |
32 TB |
With the default BLCKSZ of 8192 bytes. Partition large tables |
| Rows per table |
As many tuples as fit on 4,294,967,295 pages |
— |
| Columns per table |
1,600 |
Also limited by the tuple having to fit on one 8 KiB page. Dropped columns count too |
| Columns in a result set |
1,664 |
— |
| Field size |
1 GB |
Large values are stored out of line via TOAST |
| Indexes per table |
Unlimited |
Bounded by relations per database |
| Columns per index |
32 |
Can be raised by recompiling |
| Partition keys |
32 |
Can be raised by recompiling |
| Identifier length |
63 bytes |
Can be raised by recompiling |
| Function arguments |
100 |
Can be raised by recompiling |
| Query parameters |
65,535 |
— |
| Out-of-line (TOAST) values per table |
2^32 |
Uses a 32-bit OID. Finding a free OID slows down long before the limit |
Authentication Reference
pg_hba.conf Fields
| Field |
Values |
Description |
| TYPE |
local, host, hostssl, hostnossl, hostgssenc, hostnogssenc |
Connection type. local is a Unix socket. hostssl is TCP with TLS only |
| DATABASE |
all, a database name, sameuser, samerole, replication, @file |
Target database |
| USER |
all, a role name, +groupname, @file |
Target role. +groupname matches all members of that role |
| ADDRESS |
CIDR range, hostname, samehost, samenet |
Client address (TCP connections only) |
| METHOD |
See the next table |
Authentication method, optionally followed by key=value options |
Authentication Methods
| Method |
Description |
Status |
trust |
No authentication. Anyone who matches the line can connect |
Never use it in production |
reject |
Rejects the connection unconditionally |
Use it for deny rules |
scram-sha-256 |
SCRAM-SHA-256 challenge-response (RFC 7677). Supports channel binding |
Recommended for passwords |
md5 |
MD5 challenge-response. Was the password_encryption default up to PG 13 |
Deprecated in 18. 19 warns after each login |
password |
Cleartext password |
Only over TLS, and better not at all |
peer |
The OS user name must match the role (optionally through a user map) |
Unix sockets only |
ident |
Asks an ident server for the OS user |
Legacy |
cert |
The client must present a valid TLS certificate. The CN (or DN with clientname=DN) must match the role or a map |
TLS only |
gss / sspi |
Kerberos via GSSAPI, or Windows SSPI |
— |
ldap |
LDAP simple bind or search+bind |
— |
radius |
RADIUS over UDP |
Removed in 19 (insecure) |
pam / bsd |
PAM, or BSD Authentication |
— |
oauth |
OAuth 2.0 bearer tokens, checked by a validator module |
New in 18 |
Client sslmode Values
sslmode |
Behavior |
disable |
No encryption |
allow |
Tries a non-TLS connection first, then falls back to TLS |
prefer (default) |
Tries TLS first, then falls back to non-TLS |
require |
TLS required. The certificate is not verified |
verify-ca |
TLS required. The server certificate must chain to a trusted CA |
verify-full |
TLS required. The CA is verified and the host name must match the certificate |
Roles and Privileges Reference
Role Attributes
| Attribute |
Effect |
LOGIN |
Can authenticate and connect |
SUPERUSER |
Bypasses all permission checks, including RLS. Use it sparingly |
CREATEDB |
Can create databases |
CREATEROLE |
Can create roles and change or drop the roles it has ADMIN OPTION on (restricted since PG 16) |
REPLICATION |
Can start streaming replication and create or drop replication slots |
BYPASSRLS |
Bypasses row-level security policies |
CONNECTION LIMIT |
Maximum concurrent connections for the role |
PASSWORD |
Sets the password, hashed according to password_encryption |
VALID UNTIL |
Password expiry timestamp |
Grantable Privileges
| Object |
Grantable privileges |
| Database |
CREATE, CONNECT, TEMPORARY, ALL |
| Schema |
CREATE, USAGE, ALL |
| Table |
SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, MAINTAIN (PG 17+), ALL |
| Sequence |
USAGE, SELECT, UPDATE, ALL |
| Function |
EXECUTE, ALL |
| Foreign Server |
USAGE, ALL |
| Large object |
SELECT, UPDATE (default privileges supported since PG 18) |
Row-Level Security Policy Types
| Command |
Policy applies to |
Example clause |
SELECT |
Rows that can be read |
USING (tenant_id = current_setting('app.tenant_id')::int) |
INSERT |
Rows that can be inserted |
WITH CHECK (tenant_id = current_setting('app.tenant_id')::int) |
UPDATE |
Rows that are visible and can be changed |
Both USING and WITH CHECK |
DELETE |
Rows that can be deleted |
USING (tenant_id = current_setting('app.tenant_id')::int) |
ALL |
All commands |
Combined policy |
Audit and Crypto Extensions Reference
pgAudit Log Classes
pgAudit keeps one branch per PostgreSQL major version (pgAudit 18.x for PG 18). It must be listed in shared_preload_libraries.
pgaudit.log class |
What is logged |
READ |
SELECT, and COPY when the source is a relation or a query (for example COPY ... TO) |
WRITE |
INSERT, UPDATE, DELETE, TRUNCATE, and COPY when the destination is a relation (for example COPY ... FROM) |
FUNCTION |
Function calls and DO blocks |
ROLE |
GRANT, REVOKE, CREATE/ALTER/DROP ROLE |
DDL |
All DDL that is not in the ROLE class |
MISC |
Miscellaneous commands such as DISCARD, FETCH, CHECKPOINT, VACUUM, SET |
MISC_SET |
Miscellaneous SET commands such as SET ROLE |
ALL |
All of the above |
pgcrypto Functions
| Function |
Purpose |
crypt(password, salt) |
Password hashing: bf, md5, xdes, des, plus sha256crypt/sha512crypt (PG 18+) |
gen_salt(type [, iter]) |
Generates a salt for crypt() |
pgp_sym_encrypt(data, key [, options]) |
Symmetric PGP encryption |
pgp_sym_decrypt(data, key [, options]) |
Symmetric PGP decryption. 18.6+ accepts ignore-cipher-failure=1 to recover data hit by CVE-2026-14663 |
pgp_pub_encrypt(data, pubkey) |
Public-key PGP encryption |
pgp_pub_decrypt(data, seckey [, psw]) |
Public-key PGP decryption |
digest(data, algorithm) |
Hash (sha256, sha512, and others) |
hmac(data, key, algorithm) |
HMAC |
Security Hardening Checklist
Production security checklist
- Set
password_encryption = scram-sha-256 (the default since 14) and reset any remaining MD5 passwords. MD5 is deprecated in 18 and warns on login in 19.
- Write
pg_hba.conf with least privilege and end it with a reject rule.
- Use
hostssl (not host) for all TCP connections to enforce TLS.
- Keep
ssl_min_protocol_version at TLSv1.2 or higher.
- Use
sslmode=verify-full in client connection strings.
- Never use
trust authentication in production.
- Give each application its own role. Do not connect applications as the
postgres superuser.
- Enable Row-Level Security on tables that hold tenant-specific or regulated data.
- Install and configure
pgaudit in compliance-sensitive environments.
- Use
pgcrypto for column-level encryption of PII or secrets, with modern ciphers (AES). After 18.6, check for data hit by CVE-2026-14663.
- Encrypt the data volume at the OS or cloud-provider level (LUKS, EBS encryption), or use a TDE distribution.
- Set
log_connections (it takes a list of stages since 18) and log_disconnections = on for connection auditing.
- Set
output_plugin_libraries (18.6+) to only the logical decoding plugins you trust.
- Set
max_slot_wal_keep_size so an abandoned replication slot cannot fill the disk.
- Apply minor releases within each quarterly cycle. 18.6 alone fixed 28 security issues.
- Rotate passwords and review role memberships regularly.
Ecosystem Versions
Current releases of widely used extensions, poolers, HA tools and operators, as of 2026-09-25. Taken from each project's changelog.
| Project |
Category |
Latest version (date) |
License |
PostgreSQL support |
| pgvector |
Vector search extension (HNSW, IVFFlat) |
0.8.6 (2026-07-29) |
PostgreSQL License |
13+ |
| PostGIS |
Geospatial extension |
3.6.4 (2026-06-08). 3.7.0 in RC |
GPL-2.0 |
3.7 targets 14–19 |
| TimescaleDB |
Time-series extension |
2.30.1 (2026-09-17) |
Apache-2.0 core plus Timescale License (TSL) features |
— |
| Citus |
Distributed (sharded) PostgreSQL |
14.0.0 (2026-02-09) |
AGPL-3.0 |
— |
| pgAudit |
Audit logging |
One branch per major (18.x for PG 18) |
PostgreSQL License |
14–19 |
| PgBouncer |
Connection pooler |
1.26.0 (2026-09-23), fixes CVE-2026-19888, 6668, 6669 |
ISC |
— |
| Patroni |
HA / failover manager (etcd, Consul, ZooKeeper, Kubernetes DCS) |
4.1.5 (2026-08-12) |
MIT |
Supports the 18.6 output_plugin_libraries GUC |
| pgBackRest |
Backup and restore |
2.59.1 (2026-08-17) |
MIT |
— |
| CloudNativePG |
Kubernetes operator (CNCF Sandbox) |
1.30.1 (2026-09-23). 1.29.x EOL 2026-09-29 |
Apache-2.0 |
Operands 14–18. Default image ghcr.io/cloudnative-pg/postgresql:18.6-system-trixie |
| Percona pg_tde |
Transparent data encryption (tde_heap access method) |
See the Percona docs |
See the repo |
Percona Server for PostgreSQL 17/18 only |
Benchmarks
Unsourced performance data
Do not plan capacity from these numbers. They were estimated from vendor documentation, community benchmarks and engineering judgment. They are not controlled benchmarks with documented test conditions: hardware, software versions and methods were not recorded, and the figures predate PostgreSQL 18's AIO. Use them as rough orders of magnitude only, and run pgbench or your own workload on your own infrastructure. Explanation — Performance Characteristics covers what drives these numbers.
pgbench TPC-B-like Workload
| Hardware |
Clients |
TPS (read-write) |
TPS (read-only) |
Latency P99 |
| 4 vCPU, 16Gi, SSD |
16 |
2,500-4,000 |
15,000-25,000 |
5-10ms |
| 8 vCPU, 32Gi, NVMe |
32 |
8,000-15,000 |
50,000-80,000 |
2-5ms |
| 16 vCPU, 64Gi, NVMe |
64 |
20,000-35,000 |
100,000-150,000 |
1-3ms |
| 32 vCPU, 128Gi, NVMe |
128 |
40,000-60,000 |
200,000-300,000 |
1-2ms |
| Operation |
B-tree |
Hash |
GIN |
GiST |
BRIN |
| Point lookup |
~0.1ms |
~0.05ms |
N/A |
~0.5ms |
~1ms |
| Range scan |
~1ms |
N/A |
~5ms |
~2ms |
~0.5ms |
| Insert overhead |
Low |
Low |
High |
Medium |
Very low |
| Storage per row |
8-16 bytes |
4-8 bytes |
Variable |
Variable |
~1 byte |
Connection Scaling
| Connection count |
Without PgBouncer |
With PgBouncer |
Notes |
| 50 |
100% baseline |
100% |
No difference |
| 200 |
85-90% |
98% |
PgBouncer multiplexes |
| 500 |
60-70% |
95% |
Memory pressure without a pooler |
| 1,000 |
30-40% |
90% |
Context switching hurts throughput |
| 5,000 |
Fails (OOM) |
85% |
Connection pooling is required |
| Storage type |
WAL write throughput |
fsync latency |
Notes |
| HDD |
50-100 MB/s |
5-20ms |
Not recommended for production |
| SATA SSD |
200-500 MB/s |
0.5-2ms |
Acceptable for small deployments |
| NVMe SSD |
1-3 GB/s |
0.05-0.2ms |
Recommended for production |
| Scenario |
Replication lag |
Throughput |
Notes |
| Async, same DC |
< 10ms |
Near line-rate |
Default config |
| Async, cross-DC |
10-100ms |
Network-limited |
WAN bandwidth matters |
| Sync, same DC |
< 1ms |
70-80% of standalone |
Commit waits for the replica |
| Sync, cross-DC |
10-50ms |
20-40% of standalone |
Not recommended for write-heavy workloads |
Practical Scaling Guidance
These are soft limits (rules of thumb). The hard limits are in Limits.
| Dimension |
Soft limit |
Hard limit |
Notes |
| Database size |
10TB (comfortable) |
Unlimited |
Partitioning and backup time dominate beyond ~10TB |
| Table size |
1TB |
32TB |
Partition large tables |
| Rows per table |
1 billion |
Page-limited |
Performance degrades with bloat |
| Columns per table |
250 |
1,600 |
TOAST for wide rows |
| Indexes per table |
20 |
Unlimited |
Each index adds write overhead |
| Concurrent connections |
200-500 |
Thousands of clients (with a pooler) |
Use PgBouncer |
Sources
- PostgreSQL Versioning Policy (support matrix, EOL dates)
- PostgreSQL release notes index
- PostgreSQL 18 release notes and 18.6 release notes
- PostgreSQL 18.6, 17.11, 16.15, 15.19, 14.24 and 19 Beta 3 Released! (28 security issues, 14 EOL date)
- PostgreSQL 19 Beta 4 Released! and PostgreSQL 19 release notes (draft)
doc/src/sgml/release-18.sgml, release-19.sgml, config.sgml and limits.sgml on the REL_18_STABLE / REL_19_STABLE branches of postgres/postgres (release dates, defaults, limits; cross-checked 2026-09-25)
- endoflife.date — PostgreSQL (second source for the EOL matrix)
- pgvector CHANGELOG, PgBouncer NEWS, Patroni release notes, pgBackRest README, CloudNativePG supported releases, PostGIS NEWS, TimescaleDB CHANGELOG, Citus CHANGELOG
- pgAudit README (log classes)
- PostgreSQL docs — Client Authentication, pgcrypto, Limits, pgbench