What is a ULID? (Universally Unique Lexicographically Sortable Identifier)
A ULID is a modern 128-bit identifier designed to resolve the limitations of traditional UUIDs while remaining 100% binary-compatible with UUID storage. Proposed by Alizain Feerasta in 2016, ULID combines the time-ordered sorting of auto-incrementing integers with the decentralized generation of cryptographic UUIDs.
Unlike standard UUIDs that require 36 characters with hyphens (e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479), a ULID is encoded into 26 alphanumeric characters using Crockford's Base32 (e.g. 01ARZ3NDEKTSV4RRFFQ69G5FAV), making it compact, case-insensitive, and clean for HTTP URLs and REST APIs.
Anatomy & Bit Layout of a ULID
A ULID consists of two distinct components totaling 128 bits (16 bytes):
| Component | Character Count | Bit Width | Description |
|---|---|---|---|
| Timestamp | 10 characters | 48 bits | Big-Endian Unix Epoch timestamp in milliseconds. Supports dates up to year 10,889 AD without wrapping. |
| Randomness | 16 characters | 80 bits | Cryptographically secure pseudo-randomness (CSPRNG) generated via crypto.getRandomValues(). |
| Total | 26 characters | 128 bits | Encoded using Crockford's Base32. Completely URL-safe with zero hyphens or punctuation. |
Why Crockford's Base32?
Crockford's Base32 alphabet uses characters 0123456789ABCDEFGHJKMNPQRSTVWXYZ. This encoding was chosen specifically over Hexadecimal or Base64 for three critical reasons:
- Human Error Prevention: Excludes letters
I,L,O, andUto eliminate reading ambiguity with numbers1and0. - Accidental Profanity Defense: Excluding letter
Uprevents the unintentional generation of offensive words in public-facing identifiers. - Case Insensitivity: Both uppercase (
01ARZ...) and lowercase (01arz...) parse to identical binary values, preventing database collation bugs.
ULID vs. UUID v4 vs. UUID v7: Feature Comparison
The table below summarizes the key trade-offs between ULID, legacy random UUIDv4, and the modern IETF RFC 9562 UUIDv7:
| Metric | ULID | UUID v7 | UUID v4 |
|---|---|---|---|
| String Length | 26 characters | 36 characters | 36 characters |
| Encoding | Crockford's Base32 | Hexadecimal (8-4-4-4-12) | Hexadecimal (8-4-4-4-12) |
| Hyphens | None (URL-friendly) | 4 Hyphens | 4 Hyphens |
| Lexicographical Sorting | Yes (Natural string sort) | Yes (Hex string sort) | No (Random) |
| Timestamp Precision | 1 millisecond (48 bits) | 1 millisecond (48 bits) | None |
| Standard Status | Open Community Spec | IETF RFC 9562 | IETF RFC 4122 |
| B-Tree Index Locality | Optimal (Append-mostly) | Optimal (Append-mostly) | Poor (Page splits) |
Sub-Millisecond Monotonicity
When high-throughput systems generate multiple ULIDs within the same millisecond, a standard random generator might yield non-sequential values. Monotonic ULID implementations detect multiple calls within the identical timestamp and increment the 80-bit random component by 1. This ensures that every newly minted ULID remains strictly greater than its predecessor.
Production Code Implementation Library
// Node.js / TypeScript: npm install ulidx
import { ulid, monotonicFactory } from 'ulidx';
// Standard ULID
const id = ulid();
console.log(id); // e.g. "01ARZ3NDEKTSV4RRFFQ69G5FAV"
// Monotonic ULID for guaranteed sorting in the same millisecond
const monotonicUlid = monotonicFactory();
const id2 = monotonicUlid();
Frequently Asked Questions About ULID
Yes. You can store a ULID as a VARCHAR(26) string column for maximum readability and URL compatibility. Alternatively, you can decode the 26 Base32 characters into standard 16 bytes and store it inside a native UUID column in PostgreSQL or a BINARY(16) column in MySQL, saving 10 bytes per row.
Yes. The 80-bit random component is generated using cryptographically secure pseudo-random number generators (such as crypto.getRandomValues in browsers and Node.js or /dev/urandom in operating systems). However, because the timestamp reveals the creation time, do not use ULIDs as secret authentication tokens or passwords.
With 80 bits of cryptographic entropy, a single node would need to generate over 1 billion ULIDs within the exact same millisecond to have a 50% probability of collision (birthday problem). Monotonic generators completely prevent intra-millisecond collisions by incrementing the least significant random bits.
ULIDs are 10 characters shorter than UUIDs (26 vs 36), contain no hyphens, and use a URL-safe character set that requires no percent-encoding. Furthermore, selecting the text in a browser or terminal with a double-click selects the entire 26-character string cleanly, unlike hyphenated UUIDs.