What Is a ULID?
A ULID (Universally Unique Lexicographically Sortable Identifier) is a 128-bit identifier designed to combine the global uniqueness of a UUID with the sortability of a timestamp-based ID. It was created by Alizain Feerasta and is defined in the ULID specification.
A ULID looks like: 01ARZ3NDEKTSV4RRFFQ69G5FAV. It is always 26 characters, URL-safe, case-insensitive, and encodes a 48-bit millisecond timestamp followed by 80 bits of randomness — all in Crockford Base32 encoding.
The Internal Structure of a ULID
A ULID's 128 bits are divided into two parts:
- →Timestamp (48 bits / 10 characters): Unix millisecond timestamp. Valid until the year 10889. The first 10 characters of the ULID string encode this value, making ULIDs lexicographically sortable by creation time.
- →Randomness (80 bits / 16 characters): Cryptographically random bits generated fresh for each ULID. Provides 2⁸⁰ ≈ 1.2 × 10²⁴ possible values per millisecond.
The total 26-character string is encoded using Crockford Base32 — a variant of Base32 that excludes visually ambiguous characters (I, L, O, U) to prevent transcription errors. The alphabet is: 0123456789ABCDEFGHJKMNPQRSTVWXYZ.
Crockford Base32: Why It Matters
Douglas Crockford (creator of JSON) designed this Base32 variant specifically to be human-friendly. The excluded characters are:
- →I — looks like 1 (one)
- →L — looks like 1 (one) in many fonts
- →O — looks like 0 (zero)
- →U — excluded to avoid accidental profanity in generated strings
The result is an alphabet that is safe to display, safe to speak aloud, safe to type, and safe to include in URLs — without any percent-encoding required.
ULID vs. UUID: Key Differences
ULIDs and UUIDs solve the same problem — globally unique identifiers — but with different trade-offs:
- →Length: ULID is 26 characters; UUID is 36 characters (including hyphens). ULID is 28% shorter.
- →Sortability: ULID is lexicographically sortable by default. UUID v4 is not; UUID v7 is.
- →URL safety: ULID uses only alphanumeric characters — no hyphens, no special characters. UUID requires hyphens (safe in URLs but slightly longer).
- →Standard: UUID is an IETF RFC standard (RFC 4122, RFC 9562). ULID is a community specification without formal RFC status.
- →Database support: UUID has native types in PostgreSQL, MySQL, SQL Server. ULID is typically stored as
VARCHAR(26)orBINARY(16). - →Randomness: ULID has 80 random bits; UUID v7 has 74 random bits. Both are more than sufficient for any application.
ULID Monotonicity: Ordering Within a Millisecond
The ULID specification includes an optional monotonic mode: when multiple ULIDs are generated within the same millisecond, the random component is incremented by 1 for each subsequent ULID. This guarantees strict ordering even at very high generation rates.
Our generator uses fresh random bits for each ULID (non-monotonic mode), which is appropriate for most use cases. If you need strict monotonic ordering within a millisecond, use a server-side ULID library that supports monotonic generation.
When to Use ULID
- →Short URLs and slugs: At 26 characters with no hyphens, ULIDs are compact enough for URL paths without percent-encoding.
- →API resource identifiers: ULID's URL-safe format makes it ideal for REST API paths like
/orders/01ARZ3NDEKTSV4RRFFQ69G5FAV. - →Event sourcing: Time-ordered event IDs that can be sorted without a separate timestamp field.
- →Log correlation: Sortable IDs that let you reconstruct event timelines from logs without a centralized clock.
- →NoSQL databases: DynamoDB, Cassandra, and MongoDB all benefit from time-ordered partition keys. ULID's compact format reduces storage overhead.
Storing ULIDs in Databases
ULIDs can be stored in several ways depending on your database and requirements:
- →
VARCHAR(26): Simple, human-readable, sortable by string comparison. 26 bytes per value. - →
BINARY(16): Compact 16-byte storage. Requires encoding/decoding but saves 10 bytes per row. - →PostgreSQL
UUIDtype: ULIDs can be stored as UUIDs since both are 128-bit values. Convert with a ULID library.
For most applications, VARCHAR(26) is the simplest choice. The 10-byte overhead compared to binary storage is negligible for most table sizes.
ULID Libraries
ULID has well-maintained libraries in all major languages:
- →JavaScript:
ulidnpm package —import { ulid } from 'ulid'; ulid() - →Python:
python-ulid—from ulid import ULID; str(ULID()) - →Go:
github.com/oklog/ulid - →Rust:
ulidcrate - →PHP:
robinvdvleuten/ulidcomposer package