What is a UUID? Universally Unique Identifiers Explained
From database primary keys to API resource identifiers, UUIDs are the backbone of unique identification in distributed systems. Learn how they work, which version to use, and why you can generate trillions without a single collision.
Table of Contents
What is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit number used to uniquely identify information in computer systems. Also known as a GUID (Globally Unique Identifier) in Microsoft ecosystems, UUIDs provide a way to generate identifiers that are practically guaranteed to be unique — without requiring a central authority or coordination between the parties generating them.
UUIDs were originally standardized in RFC 4122 (2005) by the Internet Engineering Task Force (IETF) and later updated in RFC 9562 (2024). The concept traces back to Apollo Computer's Network Computing System in the 1980s, and was later adopted by the Open Software Foundation's Distributed Computing Environment (DCE). Today, UUIDs are built into virtually every programming language, database, and operating system.
Here is what a typical UUID looks like:
550e8400-e29b-41d4-a716-446655440000The key insight behind UUIDs is that the space of possible values (2128 ≈ 3.4 × 1038) is so astronomically large that randomly generated values will almost never collide. To put this in perspective, you could generate one billion UUIDs per second for 100 years and the probability of a single duplicate would still be vanishingly small.
UUID Format and Structure
A UUID is written as 32 hexadecimal digits, displayed in five groups separated by hyphens in the form 8-4-4-4-12, for a total of 36 characters (including hyphens). Despite the text representation, the underlying value is always 128 bits (16 bytes).
| Field | Hex Digits | Bits | Description |
|---|---|---|---|
| time_low | 8 | 32 | Low 32 bits of the timestamp (or random for v4) |
| time_mid | 4 | 16 | Middle 16 bits of the timestamp |
| time_hi_and_version | 4 | 16 | 4-bit version number + 12 high bits of timestamp |
| clk_seq_hi_variant | 2 | 8 | 2-bit variant + 6-bit clock sequence high |
| clk_seq_low | 2 | 8 | Low 8 bits of the clock sequence |
| node | 12 | 48 | Node ID (MAC address in v1, random in v4) |
The version number is encoded in the 4 most significant bits of the time_hi_and_version field (the 13th hex digit). For UUID v4, this digit is always 4. The variant is encoded in the 2 most significant bits of the clk_seq_hi_variant field (the 17th hex digit), which for RFC 4122 UUIDs is always 8, 9, a, or b.
UUID Versions Compared
The UUID specification defines several versions, each with a different method for generating the 128-bit value. The choice of version depends on your requirements for randomness, determinism, ordering, and privacy.
| Version | Name | Source | Deterministic? | Sortable? | Status |
|---|---|---|---|---|---|
| v1 | Time-based | Timestamp + MAC address | No | Partially* | Widely used |
| v2 | DCE Security | Timestamp + MAC + local domain | No | Partially | Rarely used |
| v3 | Name-based (MD5) | MD5 hash of namespace + name | Yes | No | Use v5 instead |
| v4 | Random | Cryptographic random numbers | No | No | Most popular |
| v5 | Name-based (SHA-1) | SHA-1 hash of namespace + name | Yes | No | Recommended for names |
| v6 | Reordered Time | v1 timestamp reordered for sorting | No | Yes | New (RFC 9562) |
| v7 | Unix Epoch Time | Unix timestamp (ms) + random | No | Yes | Recommended new |
| Nil | Nil UUID | All zeros | Yes | N/A | Sentinel value |
* UUID v1 embeds a timestamp, but the bytes are not in chronological order, so lexicographic sorting does not equal time ordering.
How UUID v4 Is Generated
UUID v4 is the most commonly used version. It is generated entirely from random (or pseudo-random) numbers, making it simple to implement and suitable for the vast majority of applications. Here is how a UUID v4 is constructed step by step:
Step-by-Step UUID v4 Generation
- 1Generate 128 random bits — Use a cryptographically secure random number generator (CSPRNG) to produce 16 random bytes.
- 2Set the version bits — Set the 4 most significant bits of byte 6 (the 7th byte) to
0100(binary for 4), marking this as UUID v4. - 3Set the variant bits — Set the 2 most significant bits of byte 8 (the 9th byte) to
10, marking this as an RFC 4122 UUID. - 4Format as hex string — Convert the 16 bytes to 32 hexadecimal characters and insert hyphens at positions 8, 12, 16, and 20.
After reserving 4 bits for the version and 2 bits for the variant, a UUID v4 has 122 bits of randomness, yielding 2122 ≈ 5.3 × 1036 possible values.
In modern browsers, UUID v4 generation is built in via crypto.randomUUID(), introduced in all major browsers in 2021. In Node.js, it has been available since v14.17 via the crypto module. For older environments, crypto.getRandomValues() can be used to fill a byte array manually.
// Browser / Node.js (v14.17+)
const uuid = crypto.randomUUID();
// → "3b241101-e2bb-4d7a-8702-9e89a5d95f11"
// Manual generation (any environment)
function generateUUIDv4() {
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant RFC4122
const hex = Array.from(bytes, b =>
b.toString(16).padStart(2, '0')
).join('');
return [
hex.slice(0, 8),
hex.slice(8, 12),
hex.slice(12, 16),
hex.slice(16, 20),
hex.slice(20, 32),
].join('-');
}Collision Probability — The Birthday Paradox
One of the most common questions about UUIDs is: "What are the odds of generating two identical ones?" The answer involves the birthday paradox — a well-known result in probability theory showing that collisions are more likely than naive intuition suggests, but still extraordinarily rare for UUIDs.
The birthday paradox tells us that after generating approximately 261 (about 2.3 × 1018) random UUID v4 values, there is a 50% chance of at least one collision. To put that number in context:
How Large Is 261?
- • Generating 1 billion UUIDs per second, it would take about 73 years to reach 261 UUIDs.
- • If every person on Earth (8 billion) generated 1 UUID per second, it would take about 9 years.
- • That is roughly 2.3 quintillion UUIDs before the collision risk reaches 50%.
- • For 1 million UUIDs (a typical large dataset), the collision probability is about 1 in 1025.
In practical terms, UUID collisions are not something you need to worry about. Any system where UUID collisions are a genuine concern is operating at a scale where other hardware and software failure modes are far more likely. That said, collision resistance depends entirely on the quality of the random number generator — always use a CSPRNG (like crypto.getRandomValues()), never Math.random().
UUIDs in Databases
UUIDs are widely used as primary keys in relational and NoSQL databases. They offer significant advantages in distributed systems but come with trade-offs you should understand before adopting them.
Advantages of UUID Primary Keys
- No coordination required: Each application instance, microservice, or client can independently generate IDs without hitting a central sequence generator.
- Safe to merge: Data from multiple databases can be merged without key conflicts — essential for multi-tenant applications, data migration, and offline-first mobile apps.
- Non-sequential: UUIDs do not reveal how many records exist or when they were created (unlike auto-increment IDs), which can be a security advantage.
- Client-side generation: IDs can be generated before the database insert, enabling optimistic UI updates and reducing round-trips.
Challenges and Solutions
| Challenge | Explanation | Solution |
|---|---|---|
| Index fragmentation | Random UUIDs cause B-tree page splits and random I/O on inserts | Use UUID v7 or ULID for time-ordered inserts |
| Storage size | 16 bytes vs 4 bytes (INT) or 8 bytes (BIGINT) | Use BINARY(16), not VARCHAR(36) — saves 20 bytes per row |
| Readability | UUIDs are harder to read and remember than sequential integers | Use short display IDs where needed, keep UUIDs as internal keys |
| JOIN performance | Larger keys mean more memory and CPU for JOIN operations | Use native UUID types (PostgreSQL, MySQL 8.0+) for optimized comparisons |
Database-Specific UUID Support
-- PostgreSQL (native UUID type)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL
);
-- MySQL 8.0+ (BINARY storage with helper functions)
CREATE TABLE users (
id BINARY(16) PRIMARY KEY DEFAULT (UUID_TO_BIN(UUID(), 1)),
name VARCHAR(255) NOT NULL
);
-- UUID_TO_BIN with swap_flag=1 rearranges bytes for better index locality
-- SQLite (TEXT storage)
CREATE TABLE users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL
);UUID vs Auto-Increment vs ULID vs Snowflake
UUIDs are not the only way to generate unique identifiers. Here is how they compare to other popular approaches:
| Feature | Auto-Increment | UUID v4 | UUID v7 | ULID | Snowflake |
|---|---|---|---|---|---|
| Size | 4-8 bytes | 16 bytes | 16 bytes | 16 bytes | 8 bytes |
| String length | 1-20 chars | 36 chars | 36 chars | 26 chars | 18-20 chars |
| Time-sortable | Yes | No | Yes | Yes | Yes |
| Decentralized | No | Yes | Yes | Yes | Partial* |
| Standard | Database-specific | RFC 4122 | RFC 9562 | Community spec | Twitter internal |
| Index performance | Excellent | Poor (random) | Good | Good | Excellent |
* Snowflake IDs require worker ID assignment, which needs some coordination.
When to choose UUID v4: You need universally unique IDs with no infrastructure and maximum portability. The random nature means no information about creation time leaks.
When to choose UUID v7: You want the benefits of UUIDs (decentralized, 128-bit, standardized) plus time-based sorting for better database index performance. UUID v7 is the recommended choice for new applications per RFC 9562.
When to choose ULID: You want a shorter, URL-friendly, time-sortable identifier. The trade-off is less universal tooling support compared to UUIDs.
When to choose Snowflake: You need compact 64-bit IDs at massive scale with guaranteed ordering. The trade-off is requiring worker ID coordination infrastructure.
Real-World Use Cases
UUIDs are used across virtually every domain of software engineering. Here are the most important applications:
Database Primary Keys
UUIDs are the default primary key type in many modern frameworks. PostgreSQL has a native UUID type with gen_random_uuid(). Django, Rails, Laravel, and Prisma all support UUID primary keys out of the box. This is especially valuable for multi-region databases, sharded architectures, and data synchronization between databases.
API Resource Identifiers
RESTful APIs use UUIDs to identify resources: /api/users/550e8400-e29b-41d4-a716-446655440000. Unlike sequential integers, UUIDs do not reveal business information (like total user count) and are not vulnerable to enumeration attacks. Stripe, GitHub, and AWS all use UUIDs in their APIs.
Distributed Systems & Microservices
In microservice architectures, each service generates its own IDs independently. UUIDs eliminate the single point of failure and bottleneck of a centralized ID generation service. Correlation IDs (also UUIDs) track requests across services for distributed tracing in tools like Jaeger, Zipkin, and OpenTelemetry.
Session & Token Management
Session tokens, CSRF tokens, password reset tokens, and email verification links commonly use UUID v4 for their unpredictability (122 random bits). OAuth 2.0 state parameters and OpenID Connect nonces are often UUIDs.
File & Object Storage
Cloud storage services (AWS S3, Azure Blob Storage, Google Cloud Storage) use UUIDs for object keys to prevent naming collisions, ensure unpredictable URLs, and distribute objects evenly across storage partitions.
Message Queues & Event Sourcing
Message brokers (Kafka, RabbitMQ, AWS SQS) use UUIDs as message IDs for deduplication and idempotent processing. In event sourcing architectures, each event gets a UUID, enabling precise replay, debugging, and audit trails.
Content-Based Addressing (UUID v5)
UUID v5 generates deterministic identifiers from a namespace and name. This is used for content addressing — the same input always produces the same UUID. Applications include deduplication, cache keys, and creating stable IDs for entities identified by natural keys (like email addresses or URLs).
// UUID v5 example — deterministic from namespace + name
import { v5 as uuidv5 } from 'uuid';
const DNS_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
const id = uuidv5('example.com', DNS_NAMESPACE);
// Always produces: "cfbff0d1-9375-5685-968c-48ce8b15ae17"
// Same input → same UUID, every time, on every machine
const id2 = uuidv5('example.com', DNS_NAMESPACE);
console.log(id === id2); // trueUUID v7 — The Future of UUIDs
UUID v7, introduced in RFC 9562 (May 2024), is a major advancement that combines the benefits of UUIDs with time-sortable ordering. It embeds a Unix timestamp in milliseconds in the most significant 48 bits, followed by random data. This design addresses the biggest complaint about UUID v4: poor database index performance.
UUID v7 Structure
0191568a-31ce-7ccc-b123-456789abcdef
Bits 0-47: Unix timestamp in milliseconds
Bits 48-51: Version (7)
Bits 52-63: Random (sub-millisecond sorting)
Bits 64-65: Variant (10 = RFC 4122)
Bits 66-127: Random
Why UUID v7 Is Better for Databases
- Sequential ordering: New UUIDs are always larger than older ones, so B-tree inserts append to the end of the index rather than causing random page splits.
- Better cache locality: Recent records cluster together in the index, which improves read performance for recent data.
- Embedded timestamp: The creation time can be extracted from the UUID without an additional column — useful for debugging and auditing.
- Backward compatible: UUID v7 uses the same 128-bit format and passes standard UUID validators, so it works with existing UUID columns and APIs.
UUID v7 support is rapidly growing across platforms. PostgreSQL 17 (2024) added uuidv7() as an extension, the uuid npm package supports it since v9.0, and Python's uuid7 package is widely used. For new projects, UUID v7 is the recommended default if time-sortable IDs are desirable.
Best Practices
✅ Do
- • Use UUID v4 as the default — it is the most widely supported and works everywhere without dependencies.
- • Consider UUID v7 for database keys — time-sorted UUIDs dramatically improve B-tree index performance.
- • Store as BINARY(16) or native UUID type — avoid VARCHAR(36) to save storage and improve comparison speed.
- • Use a CSPRNG — always generate UUIDs with
crypto.randomUUID()or equivalent cryptographic APIs. - • Compare case-insensitively — store in lowercase for consistency, but compare without regard to case.
- • Validate on input boundaries — verify UUID format when accepting from external sources (APIs, user input).
- • Use UUID v5 for deterministic IDs — when the same input should always produce the same identifier.
❌ Don't
- • Don't use Math.random() — it is not cryptographically secure and can produce predictable or duplicate UUIDs.
- • Don't rely on UUIDs for security — UUIDs are unique, not secret. Add proper authentication and authorization.
- • Don't store as VARCHAR(36) — wastes more than double the storage of BINARY(16) and is slower to compare.
- • Don't use UUID v1 in privacy-sensitive contexts — it embeds the MAC address, potentially leaking hardware identity.
- • Don't assume time ordering with v4 — random UUIDs have no temporal ordering. Use v7 if you need sorting.
- • Don't truncate UUIDs — shortening a UUID dramatically increases collision probability. Use NanoID if you need shorter IDs.
Generate UUIDs Instantly
Ready to generate UUIDs? Use our free online UUID generator to create v4, v1, or nil UUIDs with bulk generation, validation, and customizable output — all processed locally in your browser.
Open UUID GeneratorReferences
- RFC 4122— "A Universally Unique IDentifier (UUID) URN Namespace" (2005), P. Leach, M. Mealling, R. Salz, IETF
- RFC 9562— "Universally Unique IDentifiers (UUIDs)" (2024), K. Davis, B. Peabody, P. Leach, IETF — defines UUID v6, v7, and v8
- PostgreSQL Documentation — UUID Data Type and gen_random_uuid() function
- MDN Web Docs — crypto.randomUUID() — Web Crypto API
- Wikipedia— "Universally unique identifier" — history, versions, and collision analysis
- uuid npm package — JavaScript UUID generation library (v9+) supporting v1, v3, v4, v5, and v7