UUID v7 vs ULID: Which Sortable ID Should You Use?

Try the UUID Generator

UUID v7 and ULID solve the same problem in almost the same way: both produce time-sortable unique identifiers by putting a millisecond timestamp at the front and filling the rest with randomness. If you have ever wondered which one to reach for, you are not alone — they look like competitors, and in many systems they are interchangeable.

This guide compares UUID v7 and ULID head to head: how each is built, how they differ in encoding and length, how they behave as database primary keys, and which one fits your stack. You can generate either format instantly with the UUID v7 Generator or the ULID Generator while you read.

The Short Answer

  • Choose UUID v7 when you need a standards-backed identifier (RFC 9562), native database types like PostgreSQL uuid or SQL Server UNIQUEIDENTIFIER, and broad ecosystem support across languages and ORMs.
  • Choose ULID when you want a shorter, URL-friendly, case-insensitive string that is pleasant to read, copy, and put in URLs — and you do not need the UUID type or RFC compliance.

Both are excellent. For most new database-backed applications, UUID v7 is the safer default because it drops into existing UUID columns and tooling. ULID shines when the identifier is user-facing or travels through URLs and logs.

Factor UUID v7 ULID
Standard RFC 9562 Community spec
Length (string) 36 chars 26 chars
Encoding Hexadecimal (with hyphens) Crockford Base32
Underlying bits 128 128
Timestamp 48-bit Unix ms 48-bit Unix ms
Randomness 74 bits 80 bits
Sortable Yes Yes
Native DB type Yes (uuid) No (stored as text/binary)
Case-sensitive Lowercase hex Case-insensitive

How UUID v7 Is Built

UUID v7 was standardized in RFC 9562 in 2024. It packs:

  • 48 bits of Unix timestamp in milliseconds (most significant bits first)
  • 4 bits for the version (0111 = 7)
  • 2 bits for the variant
  • 74 bits of randomness

A typical UUID v7 looks like this:

018f0ecb-a94e-7000-8000-4a4197e8c642
^^^^^^^^^^^^^             ^
48-bit timestamp     version 7

Because the timestamp occupies the leading bits, two UUID v7 values generated a second apart will sort in chronological order whether you compare them as strings or as raw bytes. That single property is what makes v7 so much friendlier to databases than the older random UUID v4.

How ULID Is Built

ULID stands for Universally Unique Lexicographically Sortable Identifier. It uses the same 128 bits but splits them differently and encodes them differently:

  • 48 bits of Unix timestamp in milliseconds
  • 80 bits of randomness
  • Encoded as 26 characters using Crockford's Base32 alphabet

A typical ULID looks like this:

01HQ8M3K4E7YB2N5R9XKQ0WZ7C
^^^^^^^^^^                ^
10-char timestamp    16-char randomness

Crockford Base32 deliberately excludes ambiguous characters (I, L, O, U), so ULIDs are hard to mistranscribe and are case-insensitive. The encoding is also URL-safe with no hyphens, which is why ULIDs are popular in path segments and public-facing IDs.

Encoding and Length: The Most Visible Difference

The clearest difference between the two formats is how they look:

  • UUID v7: 36 characters, lowercase hexadecimal, four hyphens. Familiar, but verbose.
  • ULID: 26 characters, uppercase-friendly Base32, no separators. Compact and tidy.

For a public URL like /orders/{id}, a ULID produces /orders/01HQ8M3K4E7YB2N5R9XKQ0WZ7C while a UUID v7 produces /orders/018f0ecb-a94e-7000-8000-4a4197e8c642. The ULID is ten characters shorter and has no hyphens to URL-encode. If your identifiers are user-visible, that polish matters.

If you are weighing several compact formats at once, the UUID alternatives guide compares ULID, Nano ID, CUID, and short UUIDs side by side.

Database Performance: Where They Are Nearly Identical

The whole reason both formats exist is to fix the performance problem that random UUID v4 primary keys cause in B-tree indexes. Random keys scatter inserts across the entire index, triggering page splits, cache misses, and write amplification.

Both UUID v7 and ULID solve this the same way: because new IDs are time-prefixed, inserts land at (or very near) the end of the index, just like an auto-increment integer. You get:

  • No B-tree fragmentation on insert
  • Hot index pages that stay in cache
  • Sequential I/O patterns
  • The distributed-generation benefits of a 128-bit ID

The decisive practical difference is storage type, not raw performance:

  • UUID v7 stores natively in a 16-byte uuid / UNIQUEIDENTIFIER column. Compact and indexed efficiently out of the box.
  • ULID has no native column type. You either store it as a 26-character string (larger, slower to compare) or convert it to its 16-byte binary form (compact, but you lose the readable representation in the database).

For a Postgres or MySQL primary key, UUID v7's native type usually wins on simplicity. For a key-value store, document database, or anywhere you persist a string anyway, ULID's storage disadvantage largely disappears.

Ecosystem and Tooling

UUID v7 benefits from the enormous existing UUID ecosystem. Most languages ship or have well-maintained libraries:

// JavaScript / TypeScript
import { v7 as uuidv7 } from 'uuid';
uuidv7(); // "018f0ecb-a94e-7000-..."
# Python (uuid6 backport; native uuid.uuid7 is arriving in newer versions)
from uuid6 import uuid7
uuid7()
-- PostgreSQL 18+
SELECT uuidv7();

ULID also has mature libraries across JavaScript, Python, Go, Rust, and more, but it is a community specification rather than an IETF standard, so native database and platform support is less common. For deeper language coverage, see how to generate UUIDs in JavaScript, Python, Java, and Go.

Monotonicity Within the Same Millisecond

Both formats encode time to the millisecond. If you generate many IDs inside a single millisecond, the random portion keeps them unique — but plain random fill does not guarantee that IDs created later in that millisecond sort after earlier ones.

ULID's specification defines an optional monotonic mode: within the same millisecond, the random component is incremented rather than re-randomized, guaranteeing strict ordering. UUID v7 implementations can offer similar monotonic counters (RFC 9562 explicitly allows using part of the random field as a sub-millisecond counter), but the behavior depends on the library you choose. If strict intra-millisecond ordering matters, verify that your specific generator supports it.

Which One Should You Use?

Use this decision shortcut:

  • Database primary key in a relational DB? UUID v7 — it fits the native uuid type and existing migrations.
  • Public, user-facing, or URL-embedded ID? ULID — shorter, no hyphens, case-insensitive, easy to read aloud.
  • Need a recognized standard for compliance or interop? UUID v7 (RFC 9562).
  • Already storing IDs as strings (NoSQL, logs, event keys)? Either works; pick ULID for compactness or v7 for ecosystem familiarity.
  • Migrating from UUID v4? UUID v7 — same column type, same string shape, just sortable.

When you are unsure, default to UUID v7 and switch to ULID only when the identifier's readability or URL footprint becomes a real concern.

FAQ

Is ULID just UUID v7 with different encoding?

Conceptually they are very close — both are a 48-bit millisecond timestamp followed by randomness in 128 bits. The differences are the encoding (Crockford Base32 vs hex), the length (26 vs 36 characters), the split of random bits (80 vs 74), and the fact that UUID v7 is an IETF standard with a native database type while ULID is a community spec. You can generate both with the UUID v7 Generator and ULID Generator to compare them directly.

Can I convert a ULID to a UUID and back?

Yes. A ULID is 128 bits, so it maps to a UUID-shaped 16-byte value and can be rendered in the standard hyphenated hex format. The reverse also works. Note that a ULID converted to UUID form will not necessarily have a valid version nibble (it was not built to the UUID version rules), so treat the conversion as a representation change, not a true version-7 UUID.

Which is faster as a database primary key?

For insert performance, they are essentially equal — both are time-ordered, so both avoid the B-tree fragmentation that random UUID v4 causes. The practical edge goes to UUID v7 because it stores in a native 16-byte uuid column, whereas a ULID stored as a 26-character string is larger and slower to compare unless you convert it to binary.

Does UUID v7 leak information like UUID v1 did?

UUID v7 embeds a creation timestamp, so anyone holding the ID can read roughly when it was generated. Unlike UUID v1, it does not include a MAC address or machine identifier, so there is no hardware leak. ULID embeds the same millisecond timestamp, so it has the identical (mild) time-disclosure characteristic. If you must hide creation time, use random UUID v4 instead.

Are ULIDs and UUID v7 case-sensitive?

UUID v7 is conventionally written in lowercase hexadecimal, and while uppercase hex is also valid, you should normalize case before comparing. ULID uses Crockford Base32, which is explicitly case-insensitive — 01hq8m... and 01HQ8M... decode to the same value.

Should I migrate existing UUID v4 keys to UUID v7?

For high-write tables where index fragmentation hurts, switching new rows to UUID v7 can meaningfully improve insert throughput, and because v7 uses the same column type and string format as v4, the migration is low-risk. The UUID as a database primary key guide covers migration patterns and hybrid designs in detail.


Ready to try them? Generate sortable IDs with the UUID v7 Generator or the ULID Generator — instant generation, bulk export, and built-in decoders for both formats.

Generate UUIDs Instantly

Create UUID v1, v4, v5, and v7 — single or bulk generation with multiple formats.

Open UUID Generator

Related Articles