ULID Generator
Generate Universally Unique Lexicographically Sortable Identifiers — sortable by creation time
Generate ULIDs
Inspect ULID
Paste a ULID to extract its embedded timestamp and decode the format.
ULID Format
| Part | Chars | Bits | Description |
|---|---|---|---|
| Timestamp | 10 | 48 | Unix epoch in milliseconds, sortable |
| Randomness | 16 | 80 | Cryptographically random via getRandomValues() |
| Total | 26 | 128 | Crockford Base32 encoded (URL-safe) |
Alphabet: 0123456789ABCDEFGHJKMNPQRSTVWXYZ (32 chars, no I/L/O/U)
You need unique identifiers for a distributed event log, but UUID v4 breaks your database index performance and auto-increment integers leak row counts. You want the collision resistance of a UUID combined with the time-ordering of sequential IDs — without coordinating a central counter. That is exactly what ULID solves.
What Is a ULID?
ULID stands for Universally Unique Lexicographically Sortable Identifier. Introduced in 2016 by Alizain Feerasta, ULIDs pack 128 bits of information into a 26-character Crockford Base32 string that sorts lexicographically by creation time.
A typical ULID looks like: 01ARZ3NDEKTSV4RRFFQ69G5FAV
Unlike UUID v4 (which is 36 characters with hyphens and is purely random), a ULID encodes its creation timestamp in the first 10 characters, making the IDs naturally ordered when sorted as strings.
ULID Format Breakdown
Every ULID is exactly 26 characters, representing 128 bits:
- First 10 characters (48 bits): Unix timestamp in milliseconds. This covers dates from 1970 to the year 10895 — well beyond any practical concern.
- Last 16 characters (80 bits): Cryptographically random data generated by
crypto.getRandomValues().
The encoding uses the Crockford Base32 alphabet — 0123456789ABCDEFGHJKMNPQRSTVWXYZ — which intentionally excludes the letters I, L, O, and U to avoid visual confusion with digits 0 and 1.
Why Lexicographic Sorting Matters
The key advantage of ULIDs over UUID v4 is that ULIDs generated in sequence sort correctly as plain strings. If you generate 1,000 ULIDs over the course of a minute and then sort them alphabetically, they will appear in chronological order.
This property directly improves database performance. B-tree indexes work efficiently when new entries are inserted in roughly sequential order. UUID v4 inserts at random positions throughout the index, causing frequent page splits and cache misses. ULID inserts near the end — similar to an auto-incrementing integer but without requiring a centralized sequence.
Generating ULIDs in Code
// npm install ulid
import { ulid } from 'ulid';
const id = ulid();
// "01ARZ3NDEKTSV4RRFFQ69G5FAV"
// With explicit timestamp
const id2 = ulid(1469918176385);
// Monotonic factory — guarantees order within same millisecond
import { monotonicFactory } from 'ulid';
const monotonicUlid = monotonicFactory();
const a = monotonicUlid(); // "01BX5ZZKBKACTAV9WEVGEMMVRY"
const b = monotonicUlid(); // "01BX5ZZKBKACTAV9WEVGEMMVRZ"
# pip install python-ulid
from ulid import ULID
id = str(ULID())
# "01ARZ3NDEKTSV4RRFFQ69G5FAV"
# Extract timestamp
u = ULID.from_str("01ARZ3NDEKTSV4RRFFQ69G5FAV")
print(u.timestamp()) # Unix seconds (float)
print(u.datetime()) # datetime object
// go get github.com/oklog/ulid/v2
import (
"github.com/oklog/ulid/v2"
"math/rand"
"time"
)
t := time.Now()
entropy := rand.New(rand.NewSource(t.UnixNano()))
id := ulid.MustNew(ulid.Timestamp(t), entropy)
fmt.Println(id.String())
Extracting the Timestamp from a ULID
Because the first 10 characters encode a Crockford Base32 timestamp, you can decode it back to a Unix millisecond timestamp without any additional metadata. This is useful for audit logs, event sourcing, and debugging — a ULID already tells you when an event occurred.
The inspector in this tool demonstrates this: paste any valid ULID and it decodes the timestamp component and renders the ISO 8601 date.
ULID vs UUID v4 vs UUID v7
| Feature | ULID | UUID v4 | UUID v7 |
|---|---|---|---|
| Length | 26 chars | 36 chars | 36 chars |
| Sortable | Yes | No | Yes |
| URL-safe | Yes | With encoding | With encoding |
| Standard | Community | RFC 9562 | RFC 9562 |
| Timestamp extraction | Yes | No | Yes |
| Hyphens | No | Yes | Yes |
ULID and UUID v7 solve the same problem — sortable, distributed unique IDs — but ULID uses a more compact encoding and is URL-safe by default. UUID v7 has the advantage of being an official RFC standard with growing native database support (PostgreSQL 17+). For new projects, either is a good choice; ULID has broader ecosystem adoption in JavaScript, while UUID v7 is better if you need database-native functions.
Use Cases
Event sourcing: ULID works perfectly as an event ID in event-sourced systems. The natural time ordering means queries like “give me all events after event X” work correctly with simple string comparison — no secondary timestamp column needed.
Database primary keys: In systems where you cannot use PostgreSQL’s native UUID v7, ULID is the best alternative. Sortable, collision-resistant, and compact.
Distributed logs: Multiple producers writing events to a shared log will produce ULIDs that sort by time. This enables log merging without coordination.
File names: ULIDs are safe as file names on all platforms. Their lexicographic ordering means ls output is chronologically sorted.
Privacy and Security
All ULID generation runs entirely in your browser using crypto.getRandomValues() for the random component. No data is transmitted to any server. The 80-bit random component provides collision resistance equivalent to UUID v4.
Frequently Asked Questions
Is ULID safe against collision? With 80 bits of randomness per millisecond, the probability of two ULIDs generated in the same millisecond colliding is 1 in 2^80 — approximately the same as UUID v4’s collision resistance. For most distributed systems this is more than sufficient.
Can I use ULID as a database primary key?
Yes. ULID is specifically designed for this. Its sortable nature avoids the B-tree fragmentation caused by UUID v4. If you are using PostgreSQL, store it as CHAR(26) or convert to UUID format for compatibility with UUID columns.
What if two ULIDs are generated in the same millisecond? The random component handles this: two ULIDs generated in the same millisecond have independent 80-bit random parts, so collision probability remains negligible. If strict monotonic ordering within a millisecond is required, use a monotonic ULID factory (available in most libraries) that increments the random portion rather than re-randomizing it.
Is ULID an open standard? ULID has a public specification on GitHub (github.com/ulid/spec) and implementations in dozens of languages, but it is a community standard rather than an IETF RFC. UUID v7 is the standardized equivalent.