PostgreSQL 18 ships uuidv7() — a built-in SQL function that generates RFC 9562-compliant UUID v7 values directly in the database. For teams using UUID primary keys, this is the most significant change to the recommended setup since gen_random_uuid() landed in PostgreSQL 13.

What uuidv7() actually does

uuidv7() generates a 128-bit UUID whose first 48 bits are the current Unix timestamp in milliseconds. This timestamp prefix makes the values sort chronologically as plain strings, which is the property that keeps B-tree index inserts sequential.

-- Generate a UUID v7
SELECT uuidv7();
-- 019236a7-b4f2-7000-8d3e-9c1a2b3d4e5f

-- Use as a column default
CREATE TABLE events (
  id uuid DEFAULT uuidv7() PRIMARY KEY,
  payload jsonb NOT NULL,
  created_at timestamptz DEFAULT now()
);

The function produces values compliant with RFC 9562 §5.7. It includes a sub-millisecond counter in the rand_a field, matching the Method 1 monotonic behaviour described in RFC 9562 §6.2 — two calls within the same millisecond produce values that sort in generation order.

Why this matters for database performance

UUID v4 primary keys scatter inserts across the entire B-tree index. Each new row lands at a random leaf page, causing page splits as the tree grows. At millions of rows the active working set expands, cache hit rates drop, and both write throughput and range-scan performance degrade measurably.

UUID v7 eliminates this by inserting at the rightmost leaf page on every write — the same behaviour as an auto-increment integer. The timestamp prefix guarantees that newer values sort after older ones, so the index grows append-only rather than fragmenting.

This was achievable before PostgreSQL 18 through application-level generation or extensions, but it required coordinating the UUID version used in application code with database defaults. A single built-in function removes that coordination cost.

Comparing the options before PostgreSQL 18

Before uuidv7() landed, the options were:

ApproachWorks inRequires
Application-level uuid.v7()Any versionApp to set the default, not the DB
pg_uuidv7 extensionPG 11+Extension installation, superuser access
pgcrypto gen_random_uuid()PG 13+ built-inOnly generates v4 — no timestamp
uuidv7()PG 18+Nothing — built-in

For teams on managed PostgreSQL (RDS, Cloud SQL, Neon, Supabase) the extension path has historically required extra steps or was unavailable. uuidv7() as a built-in function removes that barrier entirely.

What changes in practice

New tables: Use DEFAULT uuidv7() on any UUID primary key. No change to the column type — the uuid column stores both v4 and v7 values identically.

Existing tables with v4 primary keys: Nothing needs to change. Existing rows remain valid. If you want new inserts to use v7, update the column default:

ALTER TABLE users ALTER COLUMN id SET DEFAULT uuidv7();

Rows created before the change keep their v4 values. Rows after get v7. The mixed state is valid — both are 128-bit RFC 9562 values in the same uuid column. Sort-by-ID will no longer be chronological for the legacy rows, but the index fragmentation problem stops immediately for new inserts.

Application code: If your application generates UUIDs at the application layer and passes them to the database, you can continue doing so. uuidv7() is only the database default — it fires when the column default is used (e.g. INSERT INTO events (payload) VALUES (...) without an explicit id). If your app supplies an explicit UUID, the database default is not invoked.

When to keep using UUID v4

The database default changing to uuidv7() does not mean v4 is wrong. UUID v7 embeds a 48-bit creation timestamp that anyone with the UUID can read. For primary keys on internal tables — events, audit logs, sessions — that is usually acceptable. For identifiers that appear in user-facing URLs, share links, or external API responses, the creation time disclosure may be undesirable. Use v4 for those columns.

See UUID v4 vs UUID v7 for a full breakdown of which to choose based on visibility and performance requirements.

PostgreSQL version support summary

PostgreSQL versionUUID v7 native?Recommended approach
18+YesDEFAULT uuidv7()
16–17NoApplication-level generation or pg_uuidv7
13–15NoApplication-level generation
< 13NoApplication-level generation

Further reading

Frequently asked questions

Does PostgreSQL 18 support UUID v7 natively?

Yes. PostgreSQL 18 ships a built-in uuidv7() function that generates RFC 9562-compliant UUID v7 values directly in SQL — no extensions or application-level generation required. The values store in the existing uuid column type unchanged.

How do I migrate from UUID v4 to UUID v7 in an existing database?

Existing v4 values do not need to change — keep them as-is. Add a new column or change the default for new inserts to produce v7. In PostgreSQL 18 you can set a column default to uuidv7(). For earlier versions use a trigger or application-layer default. The uuid column type accepts both versions without modification.

Can I use UUID v7 in PostgreSQL?

Yes. PostgreSQL 18 ships a native uuidv7() function; earlier versions can store any UUID this tool generates in a uuid column.

Is uuidv7() available in PostgreSQL 17?

No. The uuidv7() function is new in PostgreSQL 18. PostgreSQL 17 and earlier require either application-level UUID v7 generation or a third-party extension such as pg_uuidv7.