Tools mentioned in this article
Open the browser-based tool while you read and try the workflow immediately.
What Is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit label used to identify information in computer systems. Standardized in RFC 4122 (and updated by RFC 9562 in 2024), a UUID is typically written in this canonical form:

550e8400-e29b-41d4-a716-446655440000
Eight hex characters, then groups of four, four, four, and twelve — always 32 hex digits plus four hyphens, 36 characters total.
The fundamental promise of a UUID is that you can generate one on any machine at any time with an astronomically low probability of collision with any other UUID ever generated. This is what makes them invaluable in distributed systems where you cannot coordinate with a central ID authority.
The Complete Version Reference
The UUID specification defines multiple versions, each with a different generation strategy.
| Version | Generation Strategy | Sortable | When to Use |
|---|---|---|---|
| v1 | MAC address + timestamp | Partially | Legacy systems, audit trails |
| v2 | DCE Security (POSIX UID/GID) | No | Almost never |
| v3 | MD5 hash of namespace + name | No | Deterministic IDs from known inputs (legacy) |
| v4 | Random | No | General purpose, session IDs |
| v5 | SHA-1 hash of namespace + name | No | Deterministic IDs from known inputs |
| v6 | Timestamp + random (reordered v1) | Yes | Ordered replacement for v1 |
| v7 | Unix timestamp ms + random | Yes | Database primary keys (recommended) |
| v8 | Custom layout | Varies | Application-specific formats |
Version 1: MAC Address + Timestamp
UUID v1 encodes the MAC address of the generating machine and a 100-nanosecond timestamp. The collision risk is extremely low because the combination of machine identity and time is unique.
The downside: MAC addresses are personally identifiable. Generating v1 UUIDs in a web application can inadvertently reveal server hardware details. For this reason, v1 has largely fallen out of favor for new systems.
Version 3 and Version 5: Deterministic UUIDs
Both v3 and v5 generate a UUID from a namespace UUID and a name string using a hash function (MD5 for v3, SHA-1 for v5). Given the same inputs, you always get the same UUID output.
This is useful for creating stable identifiers from external data:
// v5 example: same URL always produces the same UUID
// Namespace UUID for URLs is defined in RFC 4122
const DNS_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
uuidv5('example.com', DNS_NAMESPACE); // always the same result
Use v5 over v3 when possible — SHA-1 is stronger than MD5, though neither is suitable for cryptographic purposes in new designs.
Version 4: The Workhorse
UUID v4 is simply 122 bits of cryptographically random data (the remaining 6 bits encode the version and variant). It is the most widely generated UUID type in existence.
// Browser / Node.js (built-in, no library needed)
const id = crypto.randomUUID(); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// Python
import uuid
print(uuid.uuid4()) # 3d6f4890-4f40-4d09-aa11-3efb...
// Go
import "github.com/google/uuid"
id := uuid.New()
Collision probability: If you generated one billion v4 UUIDs per second for 86 years, the probability of a single collision would be roughly 50%. For practical purposes, collisions are effectively impossible.
Limitation: v4 UUIDs are random, meaning they are not sortable. Inserting random UUIDs as primary keys in a B-tree index causes index fragmentation over time, which hurts write performance at scale.
Version 7: The Modern Standard
UUID v7 was standardized in RFC 9562 (May 2024). It places a Unix millisecond timestamp in the most significant bits, followed by random data.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms | ver | rand_a |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Because the timestamp is in the high bits, v7 UUIDs are lexicographically sortable by generation time. Two UUIDs generated in sequence will sort in that order — exactly what database indexes want.
// Using the 'uuidv7' package
import { uuidv7 } from 'uuidv7';
const id1 = uuidv7(); // "018e1f27-42a0-7000-8000-..."
const id2 = uuidv7(); // "018e1f27-42a1-7000-..."
// id1 < id2 (string comparison gives correct order)
UUID vs. Sequential Integer IDs
For database primary keys, the traditional choice was auto-incrementing integers (SERIAL, AUTO_INCREMENT). UUIDs change that trade-off.
| Concern | Auto-increment Integer | UUID v4 | UUID v7 |
|---|---|---|---|
| ID predictability | Guessable (enumerable) | Unpredictable | Unpredictable |
| Index performance | Excellent (sequential) | Poor (random writes) | Excellent (time-ordered) |
| Distributed generation | Requires central coordinator | Any node, any time | Any node, any time |
| Merge / replication | Complex | Simple | Simple |
| Row size overhead | 4–8 bytes | 16 bytes | 16 bytes |
| Human readability | Easy | Hard | Hard |
For most modern systems — especially those running microservices, handling multi-region deployments, or merging data from multiple sources — UUID v7 is the right default for primary keys.
Generating UUIDs in Common Languages
JavaScript / TypeScript
// v4 — built into modern browsers and Node.js 14.17+
const sessionId = crypto.randomUUID();
// v7 — install uuidv7 package
import { uuidv7 } from 'uuidv7';
const recordId = uuidv7();
Python
import uuid
# v4
print(uuid.uuid4())
# v7 — available in Python 3.12+ via third-party lib
# pip install uuid7
from uuid7 import uuid7
print(uuid7())
Go
import "github.com/google/uuid"
// v4
id := uuid.New()
// v7
id, err := uuid.NewV7()
PostgreSQL
-- v4 built-in (requires pgcrypto or pg 13+)
SELECT gen_random_uuid();
-- v7 with pgx or application-side generation
INSERT INTO users (id, email) VALUES ($1, $2)
-- pass uuidv7() from application layer
Practical Considerations
Storing UUIDs in Databases
- PostgreSQL: Use the native
UUIDtype. Stores as 16 bytes, not 36 character strings. - MySQL/MariaDB: Use
BINARY(16)for efficiency, orCHAR(36)for readability. Insert withUUID_TO_BIN(uuid, true)to preserve sortability. - SQLite: Store as
BLOB(16)orTEXT(36).
String vs. Binary Storage
A UUID as a text string ("f47ac10b-58cc-4372-a567-0e02b2c3d479") occupies 36 bytes. As raw binary, it is 16 bytes. At millions of rows, that difference is meaningful both for storage and index size.
URL-Safe UUIDs
Standard UUIDs are URL-safe because they only contain hex digits and hyphens. However, if you want an even more compact representation for use in URLs, you can remove the hyphens (32 characters) or use Base64Url encoding (22 characters).
FAQ
Is UUID v4 truly random?
UUID v4 uses 122 bits of random data (the other 6 bits are fixed for version and variant). Whether it is “truly” random depends on the random number generator. Most modern environments use a cryptographically secure pseudorandom number generator (CSPRNG), which is suitable for security-sensitive uses. crypto.randomUUID() in browsers and Node.js uses the system CSPRNG.
Should I switch all my existing v4 UUIDs to v7?
No — migrating existing IDs is usually not worth the effort. The benefits of v7 (sortability, index locality) apply to new inserts. If your system already has v4 UUIDs and index performance is acceptable, leave them. Start using v7 for new tables or new systems.
Can two machines generate the same UUID v4?
Theoretically yes, practically no. With 122 bits of randomness, you would need to generate about 2.7 × 10¹⁸ UUIDs before a 50% collision probability. At one billion UUIDs generated per second across all machines in the world, that would take over 85 years.
What is a nil UUID?
The nil UUID is all zeros: 00000000-0000-0000-0000-000000000000. It is used as a sentinel value (similar to null) to indicate “no UUID.” Some ORMs use it as a default value before an ID is assigned.
How do I validate a UUID format?
A simple regex check:
const UUID_REGEX =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
UUID_REGEX.test('550e8400-e29b-41d4-a716-446655440000'); // true
UUID_REGEX.test('not-a-uuid'); // false
For version-specific validation, check that position 14 (the version digit) matches the expected version number.