ArmorDB Logo
ArmorDB
Postgresql 18 Uuidv7 Time Ordered Uuids
PostgreSQL 18’s uuidv7() Makes Time-Ordered UUIDs a Better Default
Back to Blog
News
May 21, 2026
10 min read

PostgreSQL 18’s uuidv7() Makes Time-Ordered UUIDs a Better Default

Why PostgreSQL 18’s new uuidv7() function matters for primary keys, B-tree locality, and managed PostgreSQL rollout decisions.

AE
ArmorDB EngineeringArmorDB engineering
PostgreSQL 18uuidv7Primary Keys

If your application already uses UUIDs, PostgreSQL 18 gives you a better default than random version 4 values for new rows: uuidv7(). The reason this matters is simple. UUIDv4 is safe and convenient, but it spreads inserts across the index. UUIDv7 keeps the same distributed generation story while making new values time-ordered, which is friendlier to the B-tree indexes that PostgreSQL uses by default.

That sounds like a small change. In practice, it affects how hot indexes grow, how much random page churn your write path creates, and how much thought you have to give to primary key generation in managed PostgreSQL. For teams that want database-generated identifiers without giving up performance discipline, this is the kind of release note worth paying attention to.

Why this release matters

PostgreSQL 18 adds a built-in uuidv7() function that generates version 7 UUIDs. The release notes call them timestamp-ordered UUIDs, and the function docs explain that the timestamp combines Unix time with millisecond precision, sub-millisecond precision, and random bits. In other words, the identifier is still globally unique and still opaque enough for normal application use, but new values arrive in a roughly increasing order.

That order is useful because PostgreSQL’s default B-tree indexes are built around sortable keys. The index docs explain that B-trees can produce sorted output and that they store entries in ascending order by default. A key that grows in roughly time order is easier for the index to absorb than a fully random one.

UUIDv4, uuidv7, and bigint keys compared

Key styleGeneration patternBest strengthMain tradeoff
UUIDv4RandomVery easy to generate anywhere, with no coordinationRandom inserts are harder on B-tree locality
uuidv7Time-ordered with random bitsBetter index locality without giving up distributed generationSlightly more structured, so it is not as opaque as UUIDv4
bigint / serialCentral sequenceCompact, simple, and naturally orderedRequires a sequence source and is less convenient across systems

For many managed PostgreSQL teams, uuidv7 lands in a practical middle ground. It keeps the application architecture simple, avoids a single central allocator for every ID, and behaves more like the kind of key a B-tree prefers.

Why time order helps PostgreSQL

PostgreSQL’s B-tree implementation rewards keys that arrive in an order the tree can follow. The PostgreSQL docs for index ordering say that B-tree indexes store entries in ascending order by default, and the UUID RFC says UUIDv7 is designed so database indexes can sort it as opaque raw bytes without parsing. The same RFC also notes that time-ordered monotonic UUIDs benefit from greater database-index locality because new values are near each other in the index.

That locality matters because random inserts force the index to touch more scattered pages. Time-ordered inserts are not magic, but they usually produce a gentler growth pattern. The practical benefit is less about theoretical elegance and more about avoiding unnecessary write-path noise when the table starts getting real traffic.

What PostgreSQL 18 actually changed

The function itself is the headline. PostgreSQL 18 includes uuidv7() alongside the older uuidv4() helper, and the new function can take an optional shift interval. Most applications will not need that optional parameter, but it is available when a caller needs to shift the computed timestamp.

The important operational point is that PostgreSQL now ships a native way to generate time-ordered UUIDs inside the database. That means new projects do not need to bolt on a custom extension or rely on client-side libraries that each team implements differently. For managed PostgreSQL, reducing that variability is valuable on its own.

How to adopt uuidv7() without a risky migration

The safest adoption pattern is boring: change the default for new rows, not the historical IDs you already store. Existing UUIDv4 values can stay in place. The point is to make future inserts friendlier to your indexes, not to rewrite the past.

If you want the database to generate the key, the migration usually looks like this:

alter table orders
  alter column id set default uuidv7();

If your app currently generates UUIDs in code, you can move that responsibility into the database for new writes while keeping the column type the same. That helps the application stay portable and removes one source of version skew between services, workers, and scripts.

Rollout stepWhat to doWhy it helps
New tablesUse uuidv7 as the default for primary keysYou get the benefit from day one
Existing tablesChange only the default for future insertsNo need for a disruptive rewrite
API contractsKeep IDs opaque and treat them as identifiers onlyPrevents accidental business logic around the UUID shape
StagingTest inserts, indexes, and backup restore flowsConfirms the change is operationally boring

Where uuidv7 is not the answer

uuidv7 is a better default for many systems, but it is not a universal one. If your application uses identifiers as secrets, do not treat any UUID format as an authorization mechanism. If your bottleneck is connection pressure rather than key generation, you still need pooling. And if your slow query is caused by a bad join or a missing index, changing the UUID format will not fix it.

That is why this release should be read as a design upgrade, not a performance miracle. PostgreSQL 18 gives you a better primary-key option, but the rest of the stack still matters. If your app is connection-heavy, our PgBouncer and PostgreSQL Connection Pooling guide is the right companion read.

A practical decision guide

SituationBetter choiceWhy
New service with PostgreSQL 18uuidv7Simple default, better index locality
Existing UUIDv4 table that already worksKeep UUIDv4 for old rows, switch defaults for new rowsLow risk and no data rewrite
System that needs globally unique IDs across many writersuuidv7Distributed generation without a central sequence bottleneck
System that depends on tiny, strictly monotonic integersbigintSmaller storage and the simplest sort order

The useful question is not whether uuidv7 is universally best. It is whether the key shape matches how the table is written and read. For most modern app tables that already prefer UUIDs, PostgreSQL 18 finally gives a more index-friendly default.

Takeaway

If your team likes UUIDs because they are easy to generate and hard to collide, PostgreSQL 18’s uuidv7() gives you most of that convenience with a better write pattern for B-tree indexes. That makes it worth considering for new tables and for new defaults on existing tables.

The right migration is small: keep the type, change the default, validate in staging, and leave the old identifiers alone. That is a practical release-driven improvement, which is exactly the kind of news managed PostgreSQL teams should pay attention to.

Sources / further reading

Topic

News

Updated

May 21, 2026

Read time

10 min read

About the author

ArmorDB Engineering writes about PostgreSQL operations, security, and infrastructure decisions for teams building production apps on ArmorDB.