Skip to main content
Skip to main content
DevelopmentJuly 21, 2026 4 min read

UUID v7 vs ULID: Sortable IDs Compared

Both fix the same problem: random IDs scatter index inserts, time-ordered IDs do not. UUID v7 fits existing UUID parsing; ULID is a compact Base32 string. Pick what your stack already understands.

Generate UUID v4 or v7 locally with the UUID Generator — no upload, no account.

Why sortable IDs matter

Random primary keys scatter inserts in B-tree and LSM indexes, causing page splits and write amplification. A time-ordered identifier keeps new values dense and inserts fast.

Both UUID v7 and ULID solve this the same way: they embed a 48-bit Unix timestamp in milliseconds at the start of the value.

UUID v7

UUID v7 is defined by RFC 9562. It uses the canonical hyphenated 8-4-4-4-12 format, a version nibble, a variant field, and about 74 random bits.

Its strength is ecosystem fit: any system that parses UUIDs today understands v7, and databases increasingly add native UUIDv7 support.

RFC 9562 pins the layout down precisely: the first 48 bits hold the Unix timestamp in milliseconds, followed by the version nibble (7), the variant field, and the remaining random bits. Because the timestamp sits at the front, byte-wise comparison of the 16-byte form matches creation order.

The spec also allows a monotonic counter to replace part of the random bits when several values share one millisecond, keeping bulk inserts in a stable order.

ULID

ULID (Universally Unique Lexicographically Sortable Identifier) encodes the same 48-bit timestamp plus 80 random bits into a 26-character Crockford Base32 string, typically uppercase.

It is not an RFC or UUID standard, but it is popular in JavaScript and Go ecosystems and sorts lexicographically in the same order as creation time.

Crockford Base32 deliberately drops the letters I, L, O and U so they cannot be confused with the digits 0 and 1, which is why 128 bits fit into 26 characters instead of the 36 a hyphenated UUID needs.

The spec includes a monotonicity rule: each new ULID within the same millisecond increments a counter, so sort order stays stable under bulk generation — useful in distributed systems where several processes share one clock.

Side by side

Where they part ways is the packaging. A hyphenated UUID is 36 characters and case-insensitive; a ULID is 26 characters, conventionally uppercase, and sorts as a plain string. The embedded timestamp is the same 48-bit millisecond value in both, so a value from either format can be decoded to a creation time — for example with the timestamp converter.

PropertyUUID v7ULID
StandardRFC 9562Community spec
Format8-4-4-4-12, hyphenated26-char Crockford Base32
Random bits~7480
Case sensitivityCase-insensitiveUppercase by convention
EcosystemUUID parsers everywhereGrowing, stack-specific

What decides it

  • Use UUID v7 when your database or tooling already understands UUIDs
  • Use ULID when your stack already uses it or you want compact string keys
  • For public-facing unguessable identifiers, keep using UUID v4
  • Do not store UUIDs as strings in MySQL if you can use BINARY(16)
  • For compact keys in URLs or cache prefixes, the 26-character ULID string is shorter than a hyphenated UUID
  • Treat both as opaque: a sortable ID is not a secret, so keep v4 for tokens and unguessable values

FAQ

Q.Are ULIDs and UUID v7 compatible?

A.No, they use different encodings, so a ULID string is not a valid UUID and vice versa. A UUID is 16 bytes written as 36 hex characters with hyphens; a ULID is a 26-character Crockford Base32 string. They solve the same problem — time-ordered, collision-resistant IDs — but the formats are not interchangeable, and a parser for one will reject the other.

Q.Is ULID more sortable than UUID v7?

A.Both sort by the same 48-bit timestamp prefix, so the ordering behavior is equivalent. Lexicographic string ordering works directly on ULIDs; UUID v7 sorts when stored as bytes or compared as the canonical string. Because the timestamp comes first even in the canonical form, sorting the hyphenated string works too, but byte comparison is cheaper.

Q.Should I migrate from ULID to UUID v7?

A.Only if the migration cost is low and your ecosystem actually benefits from RFC 9562 parsing. The switch touches stored values, indexes, and any code that parses or displays IDs, so weigh that against what you gain. Both formats are valid choices; consistency within your system matters more than the format itself.

  • A pragmatic middle ground is to keep ULID where it already exists and use UUID v7 for new tables.

References

  • RFC 9562 – Universally Unique IDentifiers (UUIDs): https://www.rfc-editor.org/rfc/rfc9562
  • ULID Specification: https://github.com/ulid/spec
  • ULID JavaScript reference implementation: https://github.com/ulid/javascript
  • uuid package (v7 support): https://github.com/uuidjs/uuid

Generate UUIDs locally

v4 or v7, one at a time or in batches, in your browser.

Choose by ecosystem

If your database or ORM already handles UUIDs, v7 drops in with the least friction. If your stack is built around ULID, switching formats buys little.

Neither is a secret: keep UUID v4 for tokens and unguessable IDs, and generate either locally with the UUID Generator.

My own rule of thumb: pick the format your database and ORM already speak, then stick with it. Swapping formats later means migrating data and indexes, and that cost usually outweighs the differences.

uuid v7 vs ulidulid vs uuidsortable id generatortime ordered identifierulid formatuuid v7 database primary keyk-sortable idulid vs uuid v7distributed id generationopaque identifier