Home Developer & Utility Tools Guide

UUID v1, v4, v7, v8: which version should you use?

An opinionated tour of the UUID versions — what each one does, when it's the right tool, and where each one bites you.

You sit down to add a primary key to a new Postgres table. Autoincrement integers feel old-fashioned, so you reach for a UUID. Your ORM offers uuid_generate_v4() and uuid_generate_v7(). Your colleague mutters something about v1 leaking MAC addresses. The internet says v7 is the new default but your stack might not support it yet. So which one do you actually pick?

v4 is fine for most things. But there are real situations where it isn't, and the right answer depends on what you're indexing into, what you're caching, and whether anyone needs to sort by ID. This is the short tour: what each version exists for, when to use it, and where the footguns are.

A brief history of UUIDs

The original spec was RFC 4122, published in 2005. It defined five versions: v1 (time + node), v2 (DCE security — basically never used outside POSIX user directories), v3 (MD5 of a name), v4 (random), and v5 (SHA-1 of a name). The idea was a 128-bit identifier that any machine could mint without coordination and reasonably expect not to clash with anyone else's.

The prehistory goes further back. Apollo Computer's Network Computing System needed unique IDs for distributed objects across machines in the late 1980s — they're the reason the bit layout has fields for a node identifier and a timestamp. Most of the design constraints in v1 are 1980s constraints. They no longer apply to anything you're likely to be building, which is why the menu got more complicated over time rather than simpler.

In 2024, RFC 9562 superseded 4122 and added three more versions: v6 (a re-ordered v1 for sortability), v7 (Unix epoch milliseconds plus random), and v8 (a free-form layout for custom schemes). That's why the menu in any modern UUID library looks weird — you're seeing twenty years of accumulated answers to different problems.

The eight versions, explained

v1 — time + node

v1 packs a 60-bit timestamp (100-nanosecond intervals since 1582) and a 48-bit node identifier — historically the machine's MAC address. Two UUIDs minted on the same machine in the same 100 ns slot are disambiguated by a clock-sequence field. It works, and it's globally unique by construction.

It's largely obsolete because the MAC-address node leaks: anyone holding a v1 UUID can read off the generating machine's network card identifier. In 1995 that didn't matter; in 2026 it can be a privacy concern. Modern v1 libraries usually substitute a random node ID instead, which keeps v1's structure but loses the original "identifies the generator" property — at which point you're better off using v4 or v7.

v3 and v5 — deterministic from a name

v3 and v5 are the odd ones in the family: they're deterministic. You pass a namespace UUID and a name (any string), and the hash of those produces the UUID. The same inputs always produce the same output. v3 uses MD5; v5 uses SHA-1. Neither hash is cryptographically strong any more, but for namespace + name fingerprinting that doesn't matter — you're not asking the hash to resist preimage attacks, just to distribute inputs across the 128-bit space.

They're useful when you need a stable identifier for an external name without keeping a lookup table. A cache key derived from a request URL. A deduplication ID for log lines. A UUID for "the user account associated with this email address" without storing the email itself. RFC 4122 defined four standard namespaces (DNS, URL, OID, X.500); you can also generate a fresh v4 to use as your own private namespace.

v4 — random

v4 is 122 bits of cryptographic random plus six fixed bits identifying it as v4. That's it. No timestamp, no node, no namespace. Just entropy. It's the default for a reason: it's the simplest to reason about, the simplest to generate, leaks nothing about the generating machine, and the collision probability is effectively zero for any realistic workload.

Roughly speaking, you'd need to generate a billion v4 UUIDs per second for 85 years to have a 50% chance of one duplicate. If your application is bigger than that, you have other problems.

v6 — reordered v1

v6 takes v1's timestamp and shuffles the bytes so the most-significant timestamp bits come first, making v6 UUIDs sort in time order without further work. It's a small upgrade to v1 that almost nobody uses, because by the time you need sortable UUIDs you're better off going straight to v7.

v7 — Unix epoch ms + random

v7 is the modern recommendation for database primary keys. The first 48 bits are a Unix timestamp in milliseconds; the remaining 74 bits (after the version + variant fields) are random. The result sorts in time order, embeds no machine-identifying data, and has plenty of entropy to keep collisions negligible.

The 2024 update (RFC 9562) is when v7 became official. Postgres 18, MySQL 8.4, and most language standard libraries either ship v7 or have a well-maintained library for it. If you're starting a new project and you want UUID primary keys, this is the version to use.

v8 — free-form custom

v8 is the escape hatch. It reserves the version + variant bits and lets you put whatever you like in the remaining 122 bits. Useful if you want to bake your own structure (a sharding key prefix, a tenant ID, a custom timestamp resolution) into the UUID while staying RFC-compliant. Almost no general-purpose library will interpret your custom layout, so you'll be the one writing the parser.

When to use which

A short decision tree:

  • One-off random ID with no constraints? v4.
  • Database primary key on Postgres or MySQL? v7 (fall back to v4 with gen_random_uuid() if your stack is too old).
  • Stable cache key derived from input? v3 or v5.
  • Time-sortable event ID? v7.
  • Something exotic with a custom layout? v8.
  • Not sure? v4.

Pitfalls

v1 leaks the generating machine. A v1 UUID generated by a real MAC-address library contains the network interface ID of the machine that minted it. If those UUIDs end up in URLs, log files, or third-party systems, they're a fingerprint. This was the reason for much of the move away from v1 — most modern libraries now use a random node ID, but if you're consuming v1 UUIDs from an old system, don't assume the node bytes are random.

v4 is only as good as your RNG. v4 needs a cryptographically strong random source. Browser crypto.getRandomValues, Linux /dev/urandom, and the standard library RNGs in modern languages are all fine. Node.js before v14.17 had crypto.randomUUID issues; some embedded platforms and older JavaScript polyfills are not fine. If your environment is constrained, verify what's actually generating those random bits.

v7 ordering is per-millisecond, not per-UUID. Within a single millisecond, the random tail is exactly that — random. If you generate a thousand v7 UUIDs in one millisecond, the order between them is arbitrary. For database inserts that's fine; for an event log where you need strict ordering of two UUIDs minted back-to-back, v7 won't give it to you on its own. Some libraries add a sub-millisecond counter to fix this; our generator does.

v3 and v5 namespace fragility. The whole point of a deterministic UUID is reproducibility. If you pick a custom namespace and don't write it down, no one — including future-you — will be able to regenerate the same UUIDs from the same inputs. Pin the namespace UUID in code or in a config file, not in your head.

When to use the UUID generator on this site

The UUID generator covers v1 through v8, including a strictly monotonic v7 that handles multiple UUIDs per millisecond correctly. Bulk mode produces up to 10,000 IDs at once and exports as CSV — handy for seeding test data, pre-allocating IDs for a downstream system that doesn't have its own generator, or just grabbing a one-off when you don't want to npm install or pip install anything. It runs entirely in your browser; the bulk generator never sends those IDs to our server.

Related tools and further reading

When your service emits UUIDs in a JSON payload, the JSON formatter is a quick way to inspect the response without leaving the browser. For a different angle on entropy and randomness, the password strength checker tells you how many bits of entropy a candidate password actually has — different problem, same conceptual territory.

The authoritative source is RFC 9562 (2024), which consolidates 4122 and adds v6, v7, and v8. Worth a read if you're implementing a library; not strictly necessary if you're just picking a version for your next table.

Pick v4 if you're not sure. Reach for v7 the moment your IDs land in a database index. The other versions exist for specific jobs — if you don't have one of those jobs, you don't need to think about them.