UUID v7 vs ULID: Which Should You Use?
UUID v7 and ULID both begin with a 48-bit millisecond timestamp, so both sort by creation time. UUID v7 keeps the standard 128-bit UUID layout and its 36-character text form, which databases and libraries already support. ULID uses the same 128 bits but prints 26 Crockford Base32 characters. Choose v7 for database keys, ULID for short copyable IDs.
What the two formats share
Both are 128-bit identifiers. Both put a 48-bit Unix millisecond timestamp in the most significant bytes, so both sort by creation time when you compare them as bytes. Both fill the rest of the value from a cryptographically secure random source. Neither is a secret and neither should be used as an authentication token.
Where they differ
UUID v7 keeps the UUID layout: 48 timestamp bits, a 4-bit version field, 12 bits of rand_a, a 2-bit variant field and 62 bits of rand_b. That leaves 74 random bits. ULID spends 48 bits on the timestamp and the remaining 80 bits on randomness, with no version or variant fields.
The text form is the visible difference. A UUID v7 prints as 36 characters in five dash-separated groups of hexadecimal, for example 018f3c9a-7b2d-7c1e-8f00-1a2b3c4d5e6f. A ULID prints as 26 characters of Crockford Base32, for example 01ARZ3NDEKTSV4RRFFQ69G5FAV. Crockford Base32 leaves out I, L, O and U so the alphabet cannot be misread, and a ULID is case-insensitive.
Database and library support
PostgreSQL has a native uuid type that stores 16 bytes and compares them in byte order, so a v7 key indexes well and needs no extension. MySQL has no UUID type; the usual pattern is BINARY(16) with UUID_TO_BIN(). ULIDs are usually stored as bytea in PostgreSQL or as a 26-character text column, because no mainstream database ships a ULID type. Language support is the same story: the standard libraries of Java, Python, Go and .NET all know UUID, and ULID needs a package in most of them.
Does the sort order really hold?
Both formats sort by millisecond, not by creation order inside a millisecond. Two IDs created in the same millisecond may compare in either order. Some UUID v7 implementations use the 12 rand_a bits as a counter to keep order stable; ULID leaves that decision to the implementation. If strict ordering inside a millisecond matters, add a sequence column instead of relying on the identifier.
Which one to pick
- Primary key in PostgreSQL, MySQL or SQL Server — UUID v7. You get a native column type, byte-ordered indexes and no extra dependency.
- Public identifier in a URL, log line or support ticket — ULID. Twenty-six characters copy cleanly and survive being read aloud better than a UUID with dashes.
- Client-side generation in a browser or mobile app — either, as long as the random source is the platform CSPRNG. Both generate in the browser without a server.
- Case-sensitive systems — UUID v7. A ULID is defined as case-insensitive, so two different spellings are the same value and a case-sensitive unique index will accept both.
What neither format gives you
Neither hides the creation time. Anyone who receives the identifier can read the millisecond timestamp out of it, which leaks volume and activity patterns. Neither is unguessable: 74 and 80 random bits are far too few for a bearer token that can be attacked offline. If you need an opaque, unguessable value, generate 128 bits of randomness with no structure, as UUID v4 does.
Generate and inspect both
The UUID & ULID generator creates all three formats locally with crypto.getRandomValues() and decodes the timestamp back out of a v7 or ULID. Nothing is uploaded.
Common questions
Are UUID v7 and ULID the same value?
No. Both are 128-bit values with a 48-bit millisecond timestamp, but the remaining bits are laid out and printed differently. A UUID v7 prints as 36 hexadecimal characters and a ULID prints as 26 Crockford Base32 characters.
Which one is smaller in a database?
Both need 16 bytes. In PostgreSQL a UUID column stores 16 bytes natively, while a ULID needs a bytea column or a 26-character text column, which is larger and slower to index.
Can I use a ULID as a secret token?
No. A ULID is predictable in its first 48 bits because the timestamp is readable, and 80 random bits are not enough for a bearer token that can be attacked offline. Use 128 random bits with no structure instead.
Scan your system prompt with LochBot — free, client-side, no data sent anywhere.