ToolSite
All posts

UUID v4 Explained: How Random Are They, Really?

Learn how UUID v4 works, where the 122 random bits come from, and how collision probability compares to reality. Use our free UUID generator to create one now.

By ToolSite5 min readguides

What a UUID Is

A UUID (Universally Unique Identifier) is a 128-bit number, typically displayed as 32 hexadecimal digits grouped with hyphens:

550e8400-e29b-41d4-a716-446655440000

The format is always 8-4-4-4-12 hex digits. UUIDs are designed to be globally unique. No central authority hands them out. Two systems can independently generate UUIDs and the chance of collision is vanishingly small.

There are several UUID versions: v1 (time + MAC address), v3 (MD5 hash of a name), v4 (random), v5 (SHA-1 hash of a name), v7 (time-ordered, newer), and v8 (vendor-defined). Version 4 is the most commonly used in web applications because it requires no coordination, no clock synchronization, and no knowledge of time or namespace. You call uuid.v4() and you get 122 random bits with metadata markers.

v4 Structure: Version, Variant, and Random Bits

A UUID v4 is not entirely random. Six bits are reserved for metadata that identifies the UUID version and variant:

550e8400-e29b-4_1d4-a_716-446655440000
                ^    ^
                |    variant (2 bits, pattern 10xx)
                version (4 bits, always 0100 = 4)
  • Version nibble (4 bits): always 0100 (decimal 4). This is the 4 in the third group. Every v4 UUID has the form xxxxxxxx-xxxx-4xxx-....
  • Variant bits (2 bits): always 10 in the most significant bits of the fourth group. This means the first hex digit of that group is 8, 9, a, or b. The fourth group always starts with one of those four digits.

The remaining 122 bits are genuinely random. This is the source of the UUID's uniqueness. Out of 128 total bits, 6 are metadata and 122 are random.

Where the Randomness Comes From

UUID v4 implementations use a cryptographically secure pseudo-random number generator (CSPRNG). This is not the same as Math.random():

  • On Linux: /dev/urandom, which draws from the kernel entropy pool fed by hardware noise sources like interrupt timing, disk I/O patterns, and thermal sensor jitter.
  • In the browser: crypto.getRandomValues(), backed by the operating system's CSPRNG.
  • In Node.js: crypto.randomBytes(), similarly backed by the OS.
  • In Python: os.urandom() or uuid.uuid4(), which reads from /dev/urandom on Linux and the equivalent on other platforms.
  • In PostgreSQL: gen_random_uuid() uses OpenSSL's CSPRNG.

These sources are seeded from hardware entropy: thermal noise, timing jitter, mouse movements, keyboard timing, disk I/O patterns. The quality of the random source is what makes UUID v4 collision resistance credible. A weak random source (like a predictable PRNG seeded from the system clock) would make collisions far more likely.

Collision Odds: The Birthday Problem at Scale

The collision probability for UUID v4 is famously small. To have a 50% chance of finding at least one collision, you need to generate approximately 2.71 quintillion UUIDs (2.71 x 10^18). This comes from the birthday problem applied to a 122-bit random space:

n ≈ sqrt(2 x 2^122 x ln(1 / (1 - 0.5)))
  ≈ 2.71 x 10^18

To put that in perspective:

  • Generating 1 billion UUIDs per second, it would take about 85 years to reach the 50% collision threshold.
  • The annual global production of UUIDs across all systems combined is a rounding error on this number.
  • You are more likely to be struck by lightning multiple times than to witness a UUID v4 collision from a properly seeded CSPRNG.

However, this math assumes perfect randomness from a CSPRNG. If you use a predictable PRNG, collisions become practical. A common failure mode is a virtual machine or container that has not gathered enough entropy at boot time and produces predictable UUIDs. Always ensure your runtime has a properly seeded entropy pool.

UUIDs vs Sequential IDs in Databases

UUIDs have tradeoffs compared to auto-incrementing integers:

  • Pros: no central coordination needed, works in distributed systems, no ID exhaustion under sharding, hard to enumerate (an attacker cannot guess the next ID).
  • Cons: 128 bits is larger than a 64-bit integer (twice the storage per row, plus index overhead). Random insertion order fragments B-tree indexes because new rows are scattered across the entire key space rather than appended at the end. UUIDs are not human-readable for debugging. Saying "order 550e8400..." to a support rep is not practical.

UUID v7 addresses the index fragmentation problem with time-ordered prefixes. The first 48 bits are a Unix timestamp in milliseconds, and the remaining bits are random. This means v7 UUIDs sort roughly in creation order and do not fragment B-tree indexes. v7 is part of the newer UUID specification (RFC 9562, 2024) and is increasingly supported in database drivers and application libraries.

If you are using PostgreSQL, use the native uuid column type. It stores the 128 bits in a compact binary format. If you are using MySQL, avoid CHAR(36). Use BINARY(16) for efficient storage and indexing. String-based UUID storage in InnoDB causes unnecessary bloat and fragmentation.

When Not to Use UUIDv4

UUID v4 is not always the right choice:

  • If you need sortability by creation time, use UUID v7 or ULID instead.
  • If you need to derive the same UUID from the same input (idempotent ID generation), use UUID v5 with a namespace and a name.
  • If you are on a system with a CSPRNG that has not been properly seeded (embedded devices, early boot in containers), UUID v4 is unsafe.
  • If storage space is critical (billions of rows), the 128-bit overhead may matter.

Try it yourself: open the UUID Generator and click Generate a few times. Notice the pattern: every output has a 4 in the position after the second hyphen and one of 8, 9, a, or b as the first character of the fourth group. These are the version and variant markers that prove it is a v4 UUID. Generate 20 UUIDs and scan for duplicates. There will not be any. Copy one and paste it into the Base64 Encoder/Decoder to see the raw 128-bit representation when decoded from hex.

Related Reading