SELECT gen_random_uuid(); returns a random version 4 UUID. It has been built in since PostgreSQL 13; before that it came from the pgcrypto extension.
SELECT gen_random_uuid(); -- 86d9d9c9-8e80-4136-b903-548ba8654282
That ran on PostgreSQL 18.6. Fed those 32 hex digits as its entropy, the tool’s core returns the same UUID; the tool below draws fresh bytes in your browser instead.
The result has the uuid type, which takes 16 bytes on disk. The same value in a text column took 37 (by pg_column_size), and its index grows to match, so keep the uuid type.
Version 7 in PostgreSQL 18
PostgreSQL 18 added uuidv7(), and uuidv4() as another name for gen_random_uuid(). PostgreSQL 17 had already added functions that read a UUID’s version and time:
SELECT uuidv7(); -- 01a13cd6-b946-7ca4-8f26-f99833f5850e
SELECT uuid_extract_version(u), uuid_extract_timestamp(u)
FROM (SELECT uuidv7() AS u) AS s; -- 7 | 2026-10-14 23:54:27.527+00
SELECT uuid_extract_timestamp(gen_random_uuid()) IS NULL; -- t
SELECT uuidv7(interval '-1 day'); -- 01a137b0-5d7b-7a9c-ab37-295776364500
The tool’s decoder reads the first one as made at 2026-10-14T23:54:27.526Z, and the last a day earlier: uuidv7(interval) shifts the timestamp it writes. PostgreSQL fills the 12 bits after the version digit with a sub-millisecond fraction of the clock (RFC 9562 section 6.2, method 3), so one session’s UUIDs keep increasing even within a millisecond. As a column default:
CREATE TABLE orders (id uuid PRIMARY KEY DEFAULT uuidv7(), total numeric);
INSERT INTO orders (total) VALUES (9.99), (24.50) RETURNING id;
-- 01a13cd6-b979-7916-9240-173dc05556f1
-- 01a13cd6-b97a-77e9-8c4d-cec2246d7e84
Why v7 helps B-tree indexes
A random key lands on a random leaf page of the primary key’s index, so pages split in the middle and stay partly empty, and every insert touches a different page. Each version 7 key is larger than the last, so inserts append at the right edge and pages fill to the index’s fillfactor. A million rows into each of two tables:
CREATE TABLE t4 (id uuid PRIMARY KEY DEFAULT uuidv4(), n int);
CREATE TABLE t7 (id uuid PRIMARY KEY DEFAULT uuidv7(), n int);
INSERT INTO t4 (n) SELECT g FROM generate_series(1, 1000000) g;
INSERT INTO t7 (n) SELECT g FROM generate_series(1, 1000000) g;
-- pgstatindex() comes from the pgstattuple extension
index | size | leaf_pages | avg_leaf_density
---------+-------+------------+------------------
t4_pkey | 38 MB | 4825 | 71.53
t7_pkey | 30 MB | 3832 | 89.98
Both inserts took about 7 seconds, because both indexes fit in memory. The gap widens once an index doesn’t: then a version 4 insert may have to read its random page from disk first.
Gotchas
- A version 7 key tells anyone who holds it when the row was created, through
uuid_extract_timestampor any decoder. Use version 4 for IDs that mustn’t show that. - On PostgreSQL 12 and older,
CREATE EXTENSION pgcrypto;providesgen_random_uuid(), and theuuid-osspextension providesuuid_generate_v4(). - Casting text to
uuidchecks it:SELECT 'not-a-uuid'::uuid;fails withinvalid input syntax for type uuid: "not-a-uuid".