# What's New in PostgreSQL 18: A DBA's Perspective

> PostgreSQL 18 features and what they change for running production databases.

Tianzhou | 2026-04-30 | Source: https://www.bytebase.com/blog/what-is-new-in-postgres-18/

---

> **Note:** This post is maintained by Bytebase, an open-source database DevSecOps tool that can manage PostgreSQL. We
> will constantly update this post to include the latest Postgres 18.x releases.

| Update History | Comment          |
| -------------- | ---------------- |
| 2025/05/13     | Initial version. |

PostgreSQL 18 ships on September 25, 2025. The release moves on async I/O, UUIDv7, and a set of long-requested operational fixes — adding `NOT NULL` without a full table scan, virtual generated columns, `RETURNING` with `old`/`new` aliases, and a faster `pg_upgrade` path. This post walks each feature and what it changes for the people running these databases.

## Release Timeline

- **PostgreSQL 18 Beta 1**: May 8, 2025
- **PostgreSQL 18 Beta 2**: July 17, 2025
- **PostgreSQL 18 Beta 3**: August 14, 2025
- **PostgreSQL 18 RC 1**: September 4, 2025
- **PostgreSQL 18 Official Release**: September 25, 2025

## Asynchronous I/O

PostgreSQL 18 introduces an asynchronous I/O subsystem, selected by the new `io_method` server variable. Linux installations can use `io_uring`; every platform gets a worker-based fallback. The first wave covers reads — sequential scans, bitmap heap scans, and VACUUM. A new system view, `pg_aios`, exposes the file handles in flight.

**DBA Note:** Reads only at first. Write-heavy OLTP will not see a step change. The largest gains should land on cloud databases with network-attached storage, where I/O latency dominates wall-clock time. Re-run baseline benchmarks before assuming existing capacity numbers still hold, and watch for new failure modes specific to the chosen I/O method.

## UUIDv7

The new `uuidv7()` function returns timestamp-ordered UUIDs. `uuidv4()` is now an alias for `gen_random_uuid()`.

**DBA Note:** Time-ordered UUIDs reduce B-tree page splits and keep hot indexes warmer. For application primary keys generated outside the database, this closes a long-standing gap against sequence-based integers without giving up global uniqueness.

## Improved EXPLAIN

`EXPLAIN ANALYZE` now includes `BUFFERS` by default. `EXPLAIN ANALYZE VERBOSE` adds WAL usage, CPU statistics, and per-row averages. `EXPLAIN (WAL)`, `VACUUM`/`ANALYZE (VERBOSE)`, and the autovacuum log all gain a full WAL buffer count.

**DBA Note:** `BUFFERS` by default lowers the bar for spotting cache misses during development. WAL and per-row averages in `VERBOSE` matter most when diagnosing replication lag and autovacuum throughput — both areas where the previous output left gaps.

## NOT NULL constraints as NOT VALID

`NOT NULL` can now be added with `NOT VALID`, then validated separately:

```sql
ALTER TABLE table_name ADD CONSTRAINT constraint_name NOT NULL (column) NOT VALID;
ALTER TABLE table_name VALIDATE CONSTRAINT constraint_name;
```

The initial `ADD CONSTRAINT` skips the table scan. Validation later runs under `ShareUpdateExclusiveLock`, so `SELECT`, `INSERT`, `UPDATE`, and `DELETE` proceed normally. The constraint still rejects new NULLs from the moment it is created.

> **Note:** Before Postgres 18, the equivalent pattern was the less obvious `CHECK (column IS NOT NULL) NOT VALID`.

**DBA Note:** Adding a `NOT NULL` on a multi-terabyte table no longer requires an `ACCESS EXCLUSIVE` lock for the duration of the scan. New rows are protected immediately; historical cleanup happens on your schedule. One fewer reason to book a maintenance window.

## OLD/NEW values in RETURNING

`RETURNING` accepts changeable `old` and `new` aliases. `INSERT ... ON CONFLICT` can return prior values. `UPDATE` can return both old and new. `DELETE` can surface values produced by an `ON DELETE` row assignment.

**DBA Note:** Application code that needed before-and-after state previously read the row first or attached a trigger. The aliases fold that into the original statement, which removes a category of race conditions and a familiar set of trigger workarounds.

## Virtual generated columns

Generated columns are now virtual by default — computed on read rather than stored. `STORED` remains available when materialization is required. Stored generated columns can now be logically replicated.

**DBA Note:** Disk and write overhead drop for derived columns that do not need persistence — concatenations, formatted output, simple arithmetic. Reads pay the compute cost on each access, so expressions that are expensive or sit on hot read paths should stay `STORED`. Logical replication of stored columns lines up with what downstream consumers typically expect.

## Major version upgrades

The headline change for upgrade operators is preserved planner statistics. The newly upgraded cluster keeps its plans without waiting for `ANALYZE` to repopulate. `pg_upgrade --jobs` parallelizes pre-upgrade checks; `--swap` exchanges directories instead of copying, which is the difference between minutes and hours on large installations.

The 18 release also lands several incompatibilities. Time zone abbreviation handling, MD5 password deprecation, `VACUUM`/`ANALYZE` inheritance behavior, and `COPY FROM` end-of-file handling all changed. Each is small in isolation; together they are where existing automation tends to break.

**DBA Note:** Statistics persistence removes one of the most common post-upgrade incident patterns — query plans regressing under load while `ANALYZE` catches up. `--swap` is the lever to pull when copy time dominated past upgrade windows. Test the incompatibilities in staging; upgrades remain the place where the unread footnote causes pages.

## Further Readings

- [Postgres 18 full release notes](https://www.postgresql.org/docs/18/release-18.html)
- [Developer's perspective about Postgres 18](/blog/what-is-new-in-postgres-18-for-developer)