What’s actually different between UUID and ULID?
“Should we use UUID or ULID for our IDs?” comes up constantly in database and API design discussions. Both aim to produce a globally unique identifier, but the bit layout underneath — and in particular whether the result is sortable — differs quite a bit. This article compares UUID v4, UUID v7, and ULID and lays out when to reach for each one.
At a glance

| UUID v4 | UUID v7 | ULID | |
|---|---|---|---|
| Standardization | RFC 9562 (formerly RFC 4122) | RFC 9562 (ratified 2024) | Community spec (not an IETF standard) |
| Bit layout | 122 random bits + 6 bits for version/variant | 48-bit Unix timestamp (ms) + 74 random bits | 48-bit Unix timestamp (ms) + 80 random bits |
| String form | 36 characters (hyphenated hex, 8-4-4-4-12) |
36 characters (same hyphenated UUID format) | 26 characters (Crockford’s Base32, no hyphens) |
| Sortable by creation order | No — fully random, no ordering | Yes — timestamp leads, so lexicographic sort matches creation order | Yes — same reasoning |
| Drop-in compatibility with existing UUID columns | Native | Native (it’s a proper UUID version) | As a 26-character string, needs conversion to fit a native UUID column |
How each one works
UUID v4: 128 bits of pure randomness
UUID v4 fills 122 of its 128 bits with a cryptographically secure random value, reserving 6 bits for the version and variant markers. It carries no information about when it was created, so there’s no way to sort by generation order. In exchange, it’s about as simple as an identifier gets, with mature support across every language and database.
UUID v7: a standard UUID with a timestamp
UUID v7 is a newer UUID version standardized in RFC 9562, ratified in 2024. It packs a millisecond-precision Unix timestamp into the leading 48 bits, filling the remaining 74 bits with randomness. Because the timestamp comes first, simply sorting UUID v7 values as strings puts them in creation order — while the value stays in the familiar 8-4-4-4-12 hyphenated format, fully compatible with existing UUID columns and UUID-aware libraries.
ULID: a Base32 encoding built for lexicographic sorting
ULID (Universally Unique Lexicographically Sortable Identifier) is a community spec that predates UUID v7’s standardization. Its bit layout is very close to UUID v7’s: a 48-bit millisecond Unix timestamp followed by 80 random bits.
The main difference is the string encoding — ULID uses Crockford’s Base32 to represent the whole thing as 26 characters with no hyphens (10 for the timestamp, 16 for the randomness). The alphabet excludes visually ambiguous characters like I, L, O, and U, which makes ULIDs less error-prone to read and transcribe by hand, and shorter than a 36-character UUID — handy when it needs to sit in a URL.
Why sortability matters
When a fully random identifier like UUID v4 is used as a primary key, each newly inserted row’s ID lands at a random position within the index. That triggers frequent B-tree page splits and forces writes to touch pages that likely aren’t in cache. The bigger the table gets, the more this shows up as a real performance cost.
UUID v7 and ULID avoid this because the timestamp leads: a newly generated ID is always greater than everything generated before it, so new rows append to the end of the index — much like an auto-incrementing integer primary key — while still keeping the UUID-style benefit of being generated independently across multiple servers with no central counter.
The trade-off: embedding a timestamp isn’t free
The same property that makes UUID v7 and ULID good primary keys — encoding the creation time — becomes a liability the moment that ID is exposed outside your database. Anyone who sees the value can read off roughly when the record was created.
For that reason, avoid time-ordered IDs for password reset tokens, session identifiers, or invite codes — anywhere the value needs to be genuinely unguessable. UUID v4, or a sufficiently long random string, is the safer choice there. Time-ordered IDs are a good fit for database primary keys, event log IDs, and other internal identifiers that aren’t handed out as bearer tokens.
Summary: which one to pick
- Compatibility with existing systems matters most, and you don’t need sortability: UUID v4
- You want a sortable primary key that’s still a fully compliant UUID, compatible with existing UUID columns and libraries: UUID v7
- You want a sortable primary key and prefer a shorter string that’s easier to read and transcribe by hand: ULID
- The value will be exposed externally (tokens, session IDs, etc.) and shouldn’t reveal when it was created: UUID v4, or a random string
Try it in Torinoa Tools
You can generate and inspect all three formats with the browser-only tools below — nothing is ever sent to a server.