UUID Type
A UUID (Universally Unique Identifier) is a 128-bit value, usually written as 32 hexadecimal digits split into five groups — for example a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11. PostgreSQL has a native UUID type that stores this efficiently as raw 16 bytes rather than as text, and it's a common choice for primary keys in systems where uniqueness has to hold across more than one database or service.
Generating UUIDs
Since PostgreSQL 13, the built-in gen_random_uuid() function generates a random (version 4) UUID with no extension required — this is the simplest option for new projects.
gen_random_uuid() — built in since PostgreSQL 13
CREATE TABLE sessions (
session_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
INSERT INTO sessions (user_id) VALUES (42) RETURNING session_id;session_id -------------------------------------- f47ac10b-58cc-4372-a567-0e02b2c3d479
On older PostgreSQL versions (before 13), the equivalent functionality came from the uuid-ossp extension's uuid_generate_v4() function, which has to be enabled first with CREATE EXTENSION. You'll still see this in older codebases — the Extensions page covers enabling and using extensions like this in more depth.
The older extension-based approach
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE legacy_sessions (
session_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id INTEGER NOT NULL
);UUID vs SERIAL as a primary key
Both are legitimate primary key strategies, and the right choice depends on your system's shape. This is a genuine trade-off, not a case where one option is simply better.
UUID | SERIAL / BIGSERIAL (integer) | |
|---|---|---|
Uniqueness | Globally unique without any coordination — safe to generate on multiple servers, offline, or client-side | Unique only within one sequence — merging rows generated on different systems risks collisions |
Storage size | 16 bytes per value | 4 bytes ( |
Index size & performance | Larger indexes; random values cause more index page fragmentation | Smaller indexes; sequential values insert efficiently at the end of the index |
Sortability | A plain v4 UUID is random — sorting by it tells you nothing about insertion order | Naturally sortable by insertion order |
Guessability | Not sequentially guessable — can't easily enumerate | Sequential values are easy to guess/enumerate if exposed publicly |
UUIDis a native 128-bit type, stored as 16 bytes.gen_random_uuid()(built in since PostgreSQL 13) is the simplest way to generate one;uuid_generate_v4()from theuuid-osspextension is the older equivalent.UUIDs need no coordination across systems, but are larger and less naturally sortable than sequential integers.
Choose UUIDs for distributed generation and unguessable identifiers; choose sequential integers for compact, naturally-ordered keys within a single database.