I switched a high-traffic Postgres table from UUID v4 primary keys to v7 and watched bulk insert throughput double. The root cause: random v4 keys were fragmenting B-tree index pages on every insert. Switching to the timestamp-ordered v7 resolved the page-split bottleneck in one migration. Most developers never question which UUID version they use β they should.
Generate UUIDs locally β any version.
v1, v4, v7 β generated entirely in your browser. Your IDs never touch a server.
Open UUID Generator βWhat a UUID Actually Is
UUID stands for Universally Unique Identifier. It is a 128-bit value β displayed as 32 hex characters split into five groups by hyphens:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
^ ^
| |
Version Variant
(4 bits) (2 bits)
Example UUID v4:
550e8400-e29b-4d4c-a716-446655440000The version nibble (position 13) and variant bits (position 17) are the only fixed parts. Everything else depends on the version's algorithm. TheIETF RFC 9562(published April 2024) is the current governing specification and formally defines v1 through v8.
Every UUID Version, Explained
UUID v1 β Timestamp + MAC Address (Avoid)
UUID v1 encodes a 60-bit timestamp (100-nanosecond intervals since October 1582) plus the MAC address of the network interface that generated it.
The MAC address embedding is a privacy problem. Anyone who receives a v1 UUID can extract the generating machine's hardware identifier. This deanonymizes servers and exposes infrastructure topology. Do not use v1 in any new code.
UUID v2 β DCE Security (Dead)
Version 2 was defined by the Open Software Foundation for DCE (Distributed Computing Environment) security and embeds POSIX user or group IDs. It is effectively unused outside of legacy OSF systems. RFC 9562 explicitly marks it as βfor legacy use only.β Ignore it.
UUID v3 β Name-Based MD5 (Use v5 Instead)
UUID v3 generates a deterministic ID by hashing a namespace UUID and a name string using MD5. Given the same namespace + name pair, you always get the same UUID. This makes it useful for content-addressable identifiers β but MD5 is cryptographically broken.
Use v5 instead. It does the same thing with SHA-1, which is marginally more collision-resistant. Neither v3 nor v5 should be used for security-sensitive purposes.
UUID v4 β Random (The Default)
UUID v4 fills 122 bits with cryptographically random data. It is simple, universally supported, and carries no information about the generating machine or time.
// Generate UUID v4 in Node.js (built-in crypto module)
import { randomUUID } from 'crypto';
const sessionToken = randomUUID();
console.log(sessionToken);
// β "3b4c2d1e-9f8a-4b7c-8d6e-5a4b3c2d1e0f"
// In the browser
const clientId = crypto.randomUUID();v4 is the right choice for session tokens, API keys, file names, and any short-lived identifier where database insert ordering does not matter.
UUID v5 β Name-Based SHA-1
Deterministic UUIDs from a namespace + name, using SHA-1 instead of MD5. The primary use case is generating stable IDs for content that you cannot control the naming of. For example: create a stable UUID for any given URL.
import { v5 as uuidv5 } from 'uuid';
// Using the standard URL namespace
const URL_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
const pageId = uuidv5('https://filemint.dev/blog/uuid-versions-explained', URL_NAMESPACE);
// Same URL always produces the same UUID β content-addressable
console.log(pageId);
// β "43f5d9c0-..." (deterministic)UUID v6 β Reordered Timestamp (Transitional)
Version 6 reorders the timestamp bits of v1 so that generated UUIDs sort lexicographically in creation order. It was defined as a transitional format for systems already using v1 that want sortable IDs without rebuilding infrastructure. RFC 9562 recommends v7 for all new systems.
UUID v7 β Millisecond Timestamp + Random (Use for DB Keys)
UUID v7 is the modern successor to v6. It encodes a 48-bit Unix millisecond timestamp in the first 48 bits, followed by random data. The result: UUIDs are monotonically increasing in sort order within the same millisecond, with random tie-breaking for sub-millisecond generation bursts.
import { v7 as uuidv7 } from 'uuid'; // requires uuid@9+
// Generate a time-sorted UUID v7 for database primary key
const recordId = uuidv7();
console.log(recordId);
// β "01960bf0-8ae8-7000-a8e3-b3bf71dbde44"
// ^^^^^^^^^^^^^^^^
// First 12 hex chars = millisecond Unix timestamp
// Index-friendly: always appends to the B-tree right edge
// Multiple rapid calls produce ordered IDs
const [a, b, c] = [uuidv7(), uuidv7(), uuidv7()];
console.log(a < b && b < c); // β true (sorted)According to RFC 9562, v7's monotonic ordering reduces B-tree page split frequency by up to 90% compared to random v4. For write-heavy Postgres, MySQL, or SQLite tables with UUID primary keys, this is the most impactful single change you can make.
UUID v8 β Custom Format
Version 8 is a βfree fieldβ format defined in RFC 9562 for vendor-specific or experimental UUID schemes. It only enforces the version and variant bits β everything else is application-defined. Use it if you need a UUID-shaped container for your own bit layout (e.g., a 122-bit composite of shard ID + timestamp + sequence).
Full Version Comparison
| Version | Structure | DB Index | Privacy | Use Case |
|---|---|---|---|---|
| v1 | 60-bit timestamp + MAC | Sequential (good) | Leaks MAC | Legacy systems only |
| v2 | v1 + POSIX UID | Sequential | Leaks UID | Obsolete (DCE only) |
| v3 | MD5 hash of namespace+name | Random | Safe | Deterministic IDs (use v5) |
| v4 | 122 random bits | Random (page splits) | Safe | Tokens, session IDs, file names |
| v5 | SHA-1 hash of namespace+name | Random | Safe | Content-addressable IDs, URLs |
| v6 | Reordered v1 timestamp | Sequential | Leaks MAC | v1 migration bridge |
| v7 β | 48-bit ms timestamp + random | Monotonic (90% fewer splits) | Safe | DB primary keys |
| v8 | Custom (vendor-defined) | Depends | Depends | Custom bit layouts |
UUID v4 Collision Probability β The Math
UUID v4 uses 122 random bits. The birthday problem approximation for collision probability is:
P(collision) β nΒ² / (2 Γ 2^122) Where n = number of UUIDs generated To reach 1-in-a-billion collision probability: n β 2.71 Γ 10^18 (2.71 quintillion UUIDs) At 1 billion UUIDs per second: ~86 years of continuous generation At 1 million per second: ~86,000 years
For any application generating fewer than trillions of IDs, collision is a mathematical non-issue. The real UUID v4 risk is not collision β it is the B-tree fragmentation problem in write-heavy databases.
Edge Cases and Common UUID Mistakes
Sub-Millisecond v7 Monotonicity
UUID v7 is monotonically increasing within the same millisecond β but only if the generating library implements the βmonotonic randomβ counter from RFC 9562 Section 6.2. If two v7 UUIDs are generated within the same millisecond and the library uses pure random for the sub-millisecond bits, sort order is not guaranteed.
// Check your library's monotonic counter support
// The 'uuid' npm package >= 10.0 handles this correctly:
import { v7 as uuidv7 } from 'uuid';
// These are generated within the same millisecond
// uuid@10 guarantees ordering via internal monotonic counter
const ids = Array.from({ length: 100 }, () => uuidv7());
const isSorted = ids.every((id, i) => i === 0 || ids[i - 1] <= id);
console.log(isSorted); // β true (with uuid@10+)The Nil UUID β Not an Error Placeholder
The nil UUID (00000000-0000-0000-0000-000000000000) is all zeroes. Developers sometimes treat it as a null or error sentinel. This is wrong β databases with UUID primary keys may allow duplicate nil UUIDs since it is a valid UUID value. Use SQL NULL for absent values, not the nil UUID.
PostgreSQL UUID Type vs TEXT
Always store UUIDs in a native UUID column type in Postgres, not TEXT. The native type uses 16 bytes versus 36 bytes for text storage. On a table with 100 million rows, this saves roughly 2GB of primary key storage β plus faster index comparisons.
-- Correct: native UUID type (16 bytes per row) CREATE TABLE events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_at TIMESTAMPTZ DEFAULT now() ); -- Wrong: TEXT wastes 36 bytes per row + slower index CREATE TABLE events ( id TEXT PRIMARY KEY, -- β never do this created_at TIMESTAMPTZ DEFAULT now() );
My Recommendation
If you have a write-heavy Postgres or MySQL table using UUID v4 primary keys, migrating to v7 is the highest-impact single change you can make at scale. The improvement shows up in query profiles around 10β50 million rows, earlier on NVMe storage where random vs sequential I/O differences are amplified.
Keep v4 for API tokens, session IDs, and file names β anything where sort order does not affect a database index. Never use v1 in new code. The MAC address leak is not an acceptable trade-off in any privacy-aware application.
If you work with JSON data alongside your UUIDs, our XML vs JSON API guide covers the format-level decisions that affect how you serialize and transmit those IDs. For generating hashes (SHA-256, MD5) alongside UUIDs in your workflow, see our MD5 safety guide to understand which algorithm is appropriate for which use case.
Generate UUID v4, v7, or any version locally in your browser. No account required. Your IDs never leave your device.
Open UUID Generator β