UUID in PostgreSQL: gen_random_uuid, uuidv7 & primary keys
PostgreSQL has a native 16-byte uuid type and generates random UUIDs with the built-in gen_random_uuid() — no extension needed since version 13. Since PostgreSQL 18, uuidv7() generates time-ordered keys that keep indexes fast.
By the Withuse team · Updated
The quick answer
-- Random UUID v4 (PostgreSQL 13+, no extension) SELECT gen_random_uuid(); -- b2f7c1de-4f0a-4e0b-9b64-6a2f0c8d9e13 -- Time-ordered UUID v7 (PostgreSQL 18+) SELECT uuidv7(); -- A table with a UUID primary key CREATE TABLE orders ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), ... );
Always use the uuid type — not text
| Storage | Size per value | Validation | Compare speed |
|---|---|---|---|
uuid | 16 bytes | automatic | fast (binary) |
text / varchar(36) | ~37 bytes | none — "not-a-uuid" gets in | slower (string compare) |
The uuid type also accepts every common input format — with or without hyphens or braces, any case — and normalizes it.
Getting UUID v7 (three ways)
Random v4 keys scatter inserts across the primary-key index; at scale this causes page splits and cache misses. Time-ordered v7 keys insert append-mostly, like bigserial — the reasoning is covered in UUID v4 vs v7. Options by PostgreSQL version:
| Your situation | Solution |
|---|---|
| PostgreSQL 18+ | built-in uuidv7() |
| PostgreSQL 13–17 | pg_uuidv7 extension |
| Can't install extensions (managed DB) | generate v7 in the application (Python, JavaScript, Go) and insert it |
-- PostgreSQL 18+ CREATE TABLE events ( id uuid PRIMARY KEY DEFAULT uuidv7(), ... ); -- Bonus: extract the creation time from a v7 key (PG 18+) SELECT uuid_extract_timestamp(id) FROM events LIMIT 1;
Legacy: uuid-ossp and pgcrypto
Older tutorials tell you to CREATE EXTENSION "uuid-ossp" for uuid_generate_v4(), or pgcrypto for gen_random_uuid(). Since PostgreSQL 13, gen_random_uuid() is in core — you only need uuid-ossp today for name-based v3/v5 or timestamp v1 generation inside the database.
Practical checklist
- Column type
uuid, nevertext - New high-insert tables: prefer
uuidv7()(or app-side v7) overgen_random_uuid() - IDs exposed in URLs where creation time is sensitive: stick with v4
- Migrating from v4 to v7 needs no schema change — both live in the same
uuidcolumn
Frequently asked questions
How do I generate a UUID in PostgreSQL?
Call gen_random_uuid(), which has been part of core PostgreSQL since version 13 and needs no extension. It returns a random version 4 UUID, so SELECT gen_random_uuid(); gives you a value immediately and DEFAULT gen_random_uuid() works directly in a column definition. PostgreSQL 18 adds uuidv7() alongside it for time-ordered values, plus uuid_extract_timestamp() to read the creation time back out of a v7 identifier. On versions between 13 and 17 there is no built-in v7, so you either install the pg_uuidv7 extension or generate the value in your application and insert it. All of these produce the same native uuid type, so mixing generation strategies across tables costs nothing at the schema level.
Should I store UUIDs as uuid, text, or varchar in PostgreSQL?
Use the native uuid type. It stores the value in 16 bytes, while text or varchar(36) needs roughly 37 including the length header — more than double, and that overhead is repeated in every index that touches the column. The uuid type also validates on insert, so a typo cannot silently become a row, and it compares as an integer pair rather than character by character, which makes joins and index lookups faster. A practical bonus is normalisation: the type accepts hyphenless, braced and uppercase input but always stores and returns the canonical lowercase form, so you never end up with the same identifier stored two different ways in two different rows.
Do I still need the uuid-ossp extension?
Only for the name-based and timestamp versions. Since PostgreSQL 13, gen_random_uuid() is in core, so the most common reason to install uuid-ossp — getting uuid_generate_v4() — is gone, and the same is true of pgcrypto, which older tutorials suggest for the same function. You still need uuid-ossp if you want to compute v3 or v5 values inside the database with uuid_generate_v3() and uuid_generate_v5(), or v1 with uuid_generate_v1(). For v7, uuid-ossp does not help at all: use PostgreSQL 18's native uuidv7(), the separate pg_uuidv7 extension on earlier releases, or generate the value application-side, which is often simplest on managed databases where extensions are restricted.
Is a UUID primary key slower than bigserial in PostgreSQL?
With random v4 keys, yes, and the gap widens with table size. Because v4 values are uniformly distributed, each insert targets a random leaf of the primary key's B-tree, which spreads writes across many pages instead of appending to one. That causes more page splits, more full-page writes to the WAL, and a working set too large to stay in shared_buffers, so cache hit rates fall as the table grows. Time-ordered v7 keys remove most of that penalty: because new values sort last, inserts concentrate on the rightmost page much like bigserial. You still pay 16 bytes per key against 8 for bigint, but you gain identifiers that clients can generate offline without coordination.
Need sample UUIDs for seeds or tests? Generate up to 1,000 at once with our free UUID generator — v4, v7, and more.