Skip to content
100% in your browser. Nothing you paste is uploaded — all processing runs locally. Read more →

UUID versions: a complete reference

On this page
  1. Quick comparison
  2. Bit layouts
  3. v1 — timestamp + node
  4. v2 — DCE Security
  5. v3 — MD5 namespace
  6. v4 — random
  7. v5 — SHA-1 namespace
  8. v6 — reordered v1
  9. v7 — Unix-ms timestamp
  10. v8 — custom
  11. Nil and max
  12. Decision matrix
  13. Code: generate every version
    1. JavaScript (uuid npm)
    2. Python
    3. Go (google/uuid)
  14. Versions are not interchangeable
  15. Try the tools

UUIDs come in eight numbered versions plus the nil and max sentinels. Each is a 128-bit value with the same external shape, but the bytes are derived differently. This page is the side-by-side reference for all of them.

Quick comparison

VersionWhat it encodesSortable?Cryptographic?Use today?
160-bit timestamp (1582 epoch, 100ns ticks) + 14-bit clock seq + 48-bit node IDnonolegacy only
2DCE Security UID (UID/GID + node + timestamp)nonoalmost never
3MD5(namespace + name)n/anouse v5 instead
4122 random bitsnoyes (CSPRNG)default for one-off IDs
5SHA-1(namespace + name)n/ano (deterministic)for derived IDs
6v1 with timestamp bits reorderedyesnouse v7 instead
748-bit Unix-ms timestamp + 74 random bitsyesyes (CSPRNG)default for primary keys
8Custom layout — vendor definedvariesvariesrarely needed
nilAll zerosn/an/asentinel
maxAll ones (RFC 9562)n/an/asentinel

The two recommendations for new code:

Bit layouts

v1:  ┌──────────────┬──────────┬──────────┬─────────┬─────────────┐
     │ time_low(32) │ tm_mid(16)│ ver|tm_hi│ var|seq │ node(48)    │
     └──────────────┴──────────┴──────────┴─────────┴─────────────┘

v3/v5: ┌──────────────────────────────────────────────────────────┐
       │ MD5/SHA-1(namespace || name) → 128 bits, ver/var bits set │
       └──────────────────────────────────────────────────────────┘

v4:  ┌──────────────────────────────────────────────────────────┐
     │ 122 random bits, 4-bit version, 2-bit variant            │
     └──────────────────────────────────────────────────────────┘

v6:  ┌─────────────────────────────────────────────────────────────┐
     │ tm_hi(48) │ tm_mid(12) │ ver │ tm_lo(12) │ var │ rand(62)   │
     └─────────────────────────────────────────────────────────────┘

v7:  ┌─────────────────────────────────────────────────────────────┐
     │ unix_ms(48) │ ver │ rand_a(12) │ var │ rand_b(62)           │
     └─────────────────────────────────────────────────────────────┘

v1 — timestamp + node

The original UUID. Encodes when and where: 100-nanosecond ticks since 1582-10-15 plus the machine’s MAC address (or a random replacement).

Pros: every value is unique without coordination if node IDs are unique. Cons: lexicographic sort doesn’t match chronological — the timestamp bytes are reordered. If MAC is real, leaks identity.

Full details on the v1 page →

v2 — DCE Security

A rare beast. Encodes a POSIX UID/GID into the timestamp area. Used by Microsoft DCE / DCOM in the early ’90s. Almost no library generates them today, and almost no system requires them. Don’t use unless you have a specific DCE compatibility requirement.

v3 — MD5 namespace

Hash a UUID namespace + a name string with MD5, set version = 3. Same input always produces the same UUID.

Pros: deterministic — same input → same UUID, no storage of the mapping needed. Cons: MD5 has known collisions for adversarial inputs. Use v5 instead for new code.

Full details on the v3 page →

v4 — random

122 bits of cryptographic randomness. The default unless you have a specific reason to choose otherwise.

Pros: trivially generated, no coordination, astronomically unlikely to collide. Cons: random insertion fragments B-tree primary-key indexes. For DB keys, prefer v7.

v5 — SHA-1 namespace

Same idea as v3, but with SHA-1 instead of MD5. Faster than v3 on modern CPUs and slightly better collision properties — though still not cryptographically secure for adversarial inputs.

When to use: stable identifiers derived from a stable input (lowercase email, normalized URL, etc.). Two services can independently arrive at the same UUID without coordinating.

Full details on the v5 page →

v6 — reordered v1

Standardized in RFC 9562. Takes v1’s timestamp bits and reorders them to make lexicographic sort match chronological. Solves v1’s biggest practical issue.

Use only for migrating from v1. For new code, v7 is simpler and avoids v1’s node-ID legacy entirely.

v7 — Unix-ms timestamp

The modern default for time-ordered IDs. 48-bit Unix millisecond timestamp at the start (in natural byte order), 74 random bits after the version/variant.

Pros: lexicographic sort = chronological. Excellent as DB primary key — see UUID as primary key. RFC standard, broad support arriving in language standard libraries (Python 3.13, .NET 9, Postgres 18).

Cons: timestamp is visible in the ID. If the creation time is sensitive, use v4.

Full details on the v7 page →

v8 — custom

A “do whatever you want” version. RFC 9562 defines only the version/variant bits; the other 122 bits are vendor-defined. Useful for embedding application-specific data (sharding keys, tenant IDs, hash prefixes) while remaining a valid UUID.

Use only when you’ve designed a specific layout and have a reason to encode it as a UUID rather than a separate field. v8 isn’t a casual choice — most code that processes UUIDs assumes one of v1–v7’s layouts.

Nil and max

nil: 00000000-0000-0000-0000-000000000000
max: ffffffff-ffff-ffff-ffff-ffffffffffff

Both are RFC-reserved sentinels. No legitimate generator produces them. Useful as “not yet assigned” markers in databases or “upper bound” sentinels in range queries.

Full details on nil and max →

Decision matrix

What do you need?
├── A unique ID, nothing else                → v4 (random)
├── A unique ID for a database primary key   → v7 (time-ordered)
├── A reproducible ID from stable input      → v5 (SHA-1 namespace)
├── A timestamp encoded inline               → v7 (forward) or v1 (legacy)
├── A reserved "no value" marker             → nil
├── Custom application-defined layout        → v8 (rarely)
└── Anything else                            → v4 unless you can name the constraint

Code: generate every version

JavaScript (uuid npm)

import { v1, v3, v4, v5, v7, NIL } from "uuid";

v1();                                     // time-based
v3("name", v3.URL);                       // MD5 namespace
v4();                                     // random
v5("name", v5.URL);                       // SHA-1 namespace
v7();                                     // Unix-ms timestamp
NIL;                                      // "00000000-..."

Python

import uuid

uuid.uuid1()                              # time-based
uuid.uuid3(uuid.NAMESPACE_URL, "name")    # MD5 namespace
uuid.uuid4()                              # random
uuid.uuid5(uuid.NAMESPACE_URL, "name")    # SHA-1 namespace
uuid.uuid7()                              # Python 3.13+
uuid.UUID(int=0)                          # nil

Go (google/uuid)

v1, _ := uuid.NewUUID()                            // time-based
v3   := uuid.NewMD5(uuid.NameSpaceURL, []byte("name"))
v4   := uuid.New()                                  // random
v5   := uuid.NewSHA1(uuid.NameSpaceURL, []byte("name"))
v7, _ := uuid.NewV7()
nil  := uuid.Nil

Versions are not interchangeable

Two UUIDs of different versions encoding the same input are different values. v3(“example”, URL_NS) ≠ v5(“example”, URL_NS). Don’t mix versions in a single column unless you have a reason — pick one per use case.

The version is detectable from the value itself (the 13th hex character is the version number), so a defensive check is easy:

import { version } from "uuid";
version("01928a47-3b30-7c5e-9d1a-f0b8c4a7e923");  // 7

Try the tools