Heading image for post: PostgreSQL 18's UUIDv7: Faster and Secure Time-Ordered IDs

PostgreSQL

PostgreSQL 18's UUIDv7: Faster and Secure Time-Ordered IDs

Profile picture of Vinicius Negrisolo

PostgreSQL 18 dropped last month with a bunch of exciting updates. While the performance improvements are always welcome, there's one developer-friendly feature that deserves the spotlight: native support for UUIDv7. This new format might change how model your database.

Why UUID for Primary Keys?

I avoid using sequential IDs for primary keys because they often end up in URLs and APIs. Exposing sequential IDs opens your application to sequential ID attacks (also known as enumeration attacks), where malicious actors can easily guess valid IDs and find out information you don't not want to turn public, like the number of users or orders you have in the database. Or in case your app misses proper authorization in a few endpoints they can access resources they shouldn't. I am all for making the app more secure so that's why I've been using UUIDv4 for quite some time already.

I know what you're thinking: random long values like UUIDv4 aren't ideal for URLs. And you're right - they're fine for internal URLs and APIs, but if you need better URLs for SEO, you'll want to implement slugs and possibly redirects when URLs change. Still, it's better to take that extra step than to compromise security with sequential integers.

UUID v4 vs v7: What's the Difference?

UUIDv4 generates completely random identifiers like e6b5a54f-ff66-4151-88f2-e5e2bed0e84e. They're secure and unpredictable, but they're also very random. This randomness can hurt database performance because new rows get scattered across index pages instead of being added sequentially.

UUIDv7 is different. It generates similar-looking random values, but with a crucial twist: the first portion encodes a timestamp from when the row was created. Let's see this in action:

CREATE TABLE users (
  id uuid DEFAULT uuidv7() PRIMARY KEY,
  email VARCHAR(100) UNIQUE NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
);

INSERT INTO users (email) VALUES ('darth@example.com');
INSERT INTO users (email) VALUES ('luke@example.com');
INSERT INTO users (email) VALUES ('anakin@example.com');
INSERT INTO users (email) VALUES ('princess@example.com');
INSERT INTO users (email) VALUES ('han@example.com');

SELECT * FROM users ORDER BY id DESC;

Here's the response:

id email created_at
0199e46c-4b68-765f-ad72-d388d2e2ef22 han@example.com 2025-10-14 20:32:06.504149-04
0199e46c-4718-78be-8bb2-fb4ffcf75135 princess@example.com 2025-10-14 20:32:05.400298-04
0199e46c-42ae-7bc8-a420-b1552b517923 anakin@example.com 2025-10-14 20:32:04.270387-04
0199e46c-3e68-710b-913a-9ddb182622bf luke@example.com 2025-10-14 20:32:03.175766-04
0199e46c-3a7b-738a-bf98-5b5de13c753d darth@example.com 2025-10-14 20:32:02.170771-04

PostgreSQL 18 now includes the uuidv7() function out of the box. I ran each insert statement roughly one second apart. Notice how the IDs naturally sort in chronological order?

The structure breaks down like this:

0199e46c-4b68-765f-ad72-d388d2e2ef22

0199e46c     => Timestamp (milliseconds)
4b68         => Timestamp (milliseconds continued)
7            => UUID version (always 7)
65f          => Sub-millisecond precision / Random
a            => Variant + Random
d72          => Random
d388d2e2ef22 => Random

Key Benefits of UUIDv7

  1. Chronological sorting: Sort by ID and get results in time order automatically — no need for a separate created_at column with an index.

  2. Better index performance: Unlike UUIDv4's random scatter, UUIDv7's time-based prefix means consecutive inserts land on the same index pages, reducing fragmentation and improving performance.

  3. Collision resistance: The random bits ensure that even if multiple rows are created in the same millisecond, they'll still have unique IDs.

  4. Security maintained: The IDs are still non-sequential and unpredictable enough to prevent enumeration attacks.

If you're starting a new project or planning a migration, UUIDv7 gives you the best of both worlds: the security of random UUIDs with the performance characteristics of sequential IDs.

If you want to know more here are the release notes for PostgreSQL 18.

Need Help with Your Database or Application?

Whether you're migrating to PostgreSQL 18, optimizing your database schema, or building modern web and mobile applications, we're here to help. At Hashrocket, we specialize in PostgreSQL and other databases, along with Elixir, Phoenix, Ruby on Rails, React, and React Native. Get in touch and let's talk about how we can help you build better software.

More posts about SQL PostgreSQL

  • Adobe logo
  • Barnes and noble logo
  • Aetna logo
  • Vanderbilt university logo
  • Ericsson logo

We're proud to have launched hundreds of products for clients such as LensRentals.com, Engine Yard, Verisign, ParkWhiz, and Regions Bank, to name a few.

Let's talk about your project