ULID Generator — Universally Unique Lexicographically Sortable Identifiers
ULID is a 26-character sortable unique identifier that embeds a millisecond timestamp prefix. Unlike UUID v4, ULIDs sort chronologically. Here's how ULIDs work and when to use...
ULID (Universally Unique Lexicographically Sortable Identifier) solves a key limitation of UUID v4: random IDs don’t sort chronologically. A ULID embeds a millisecond Unix timestamp in its first 10 characters, so ULIDs generated at different times sort correctly without a separate created_at column.
Use the UUID Generator to generate UUID v4 identifiers.
ULID format
A ULID is a 26-character string using Crockford base32 encoding:
01ARZ3NDEKTSV4RRFFQ69G5FAV
└──────────┘└────────────────┘
10 chars 16 chars
48-bit time 80-bit random
- First 10 characters: 48-bit Unix timestamp in milliseconds
- Last 16 characters: 80 bits of cryptographic randomness
- Total: 128 bits (same as UUID)
- Alphabet:
0123456789ABCDEFGHJKMNPQRSTVWXYZ(Crockford base32 — excludes I, L, O, U to avoid ambiguity)
Example output:
01HVNKQ3RTMWJBQXCZ4GFHPR9Y
01HVNKQ3RTMWJBQXCZ4GFHPR9Z
01HVNKQ3RTMWJBQXCZ4GFHPRA0
These three ULIDs were generated in the same millisecond. They sort correctly because the random component increments lexicographically within the same millisecond.
Generating ULIDs in code
JavaScript
import { ulid, monotonicFactory } from 'ulid';
// Basic generation:
const id = ulid();
// "01HVNKQ3RTMWJBQXCZ4GFHPR9Y"
// With explicit timestamp:
const id = ulid(Date.now());
// Monotonic factory — guaranteed sort order within same millisecond:
const monotonicUlid = monotonicFactory();
const id1 = monotonicUlid();
const id2 = monotonicUlid();
const id3 = monotonicUlid();
// id1 < id2 < id3 (always, even within the same millisecond)
Monotonic mode is important in high-throughput systems where multiple IDs are generated per millisecond. Without it, two IDs in the same millisecond could have the same timestamp prefix and sort randomly by their random component.
Node.js install
npm install ulid
Python
from ulid import ULID
# Generate ULID:
u = ULID()
print(str(u)) # "01HVNKQ3RTMWJBQXCZ4GFHPR9Y"
print(u.timestamp()) # 1709551234.567 (Unix timestamp)
print(u.datetime) # datetime object
# From existing timestamp:
import datetime
u = ULID.from_datetime(datetime.datetime.now())
pip install python-ulid
Go
package main
import (
"fmt"
"github.com/oklog/ulid/v2"
"math/rand"
"time"
)
func main() {
entropy := rand.New(rand.NewSource(time.Now().UnixNano()))
ms := ulid.Timestamp(time.Now())
id := ulid.MustNew(ms, entropy)
fmt.Println(id.String()) // "01HVNKQ3RTMWJBQXCZ4GFHPR9Y"
fmt.Println(ulid.Time(id.Time())) // time.Time
}
Extracting the timestamp from a ULID
import { decodeTime } from 'ulid';
const id = '01HVNKQ3RTMWJBQXCZ4GFHPR9Y';
const timestamp = decodeTime(id);
// 1709551234567 (milliseconds since Unix epoch)
const date = new Date(timestamp);
// "2024-03-04T14:20:34.567Z"
This is a key ULID advantage: you can extract the creation time from the ID itself without a separate database column.
ULID in databases
PostgreSQL
-- Store as CHAR(26) or TEXT:
CREATE TABLE events (
id CHAR(26) DEFAULT '' NOT NULL,
type TEXT NOT NULL,
payload JSONB,
PRIMARY KEY (id)
);
-- With application-generated ULIDs:
INSERT INTO events (id, type, payload)
VALUES ('01HVNKQ3RTMWJBQXCZ4GFHPR9Y', 'user.created', '{"user_id": 1}');
-- Sorting by ID = sorting by creation time:
SELECT * FROM events ORDER BY id DESC LIMIT 10;
-- Range queries by time using ULID prefix:
-- All events from March 2024:
SELECT * FROM events
WHERE id >= '01HWKM00000000000000000000'
AND id < '01HXABG00000000000000000000';
Index performance vs UUID
UUID v4 causes B-tree index fragmentation because new values insert randomly throughout the tree. ULIDs insert near the end of the index (newest values are largest), which is the same access pattern as auto-increment integers — much better for insert performance on large tables.
-- Benchmark insert performance (PostgreSQL, 10M rows):
-- UUID v4 primary key: ~82,000 inserts/sec
-- ULID primary key: ~94,000 inserts/sec (+15%)
-- BIGSERIAL (int8): ~97,000 inserts/sec (+18%)
ULID vs UUID comparison
| Property | UUID v4 | ULID |
|---|---|---|
| Sortable | No | Yes (chronological) |
| Length | 36 chars | 26 chars |
| Alphabet | Hex + dashes | Crockford base32 |
| Timestamp in ID | No | Yes (ms precision) |
| Standard | RFC 4122 | ULID spec |
| DB native type | Yes (Postgres UUID) | No (store as CHAR) |
| Library support | Universal | Good (major languages) |
| Entropy | 122 bits | 80 bits random + 48 bits time |
ULID vs KSUID
KSUID (K-Sortable Unique Identifier) is an alternative sortable ID format:
| Property | ULID | KSUID |
|---|---|---|
| Length | 26 chars | 27 chars |
| Timestamp precision | Milliseconds | Seconds |
| Timestamp epoch | Unix (1970) | Custom (2014) |
| Random bits | 80 bits | 128 bits |
| Encoding | Base32 | Base62 |
KSUID has more random bits but second-level precision (not millisecond). ULID is better for most use cases.
UUID v7 — the standardized version of ULID
RFC 9562 (2024) introduced UUID v7, which is essentially ULID in UUID format:
UUID v7: 018e7b5a-5218-7xxx-yxxx-xxxxxxxxxxxx
└──────────────┘
48-bit ms timestamp
UUID v7:
- Same chronological sorting as ULID
- Standard UUID format (36 chars, UUID type in databases)
- Becoming available in libraries and databases (PostgreSQL 17 supports it)
For new projects where database UUID type support matters, UUID v7 may be preferable to ULID. For projects already using ULID or needing the Crockford base32 format, ULID remains a solid choice.
Related tools
- UUID Generator — generate UUID v4 identifiers
- UUID v4 Generator — UUID format explained
- NanoID vs UUID — shorter URL-safe IDs
Related posts
- UUID v4 vs v7 for Databases: The Benchmark You Need — UUID v4 fragments your primary key index. UUID v7 fixes it with millisecond-orde…
- CUID2 — Collision-Resistant IDs Better Than UUID v4 — CUID2 generates secure, URL-safe, database-friendly IDs with better collision re…
- NanoID vs UUID — Which Unique ID Generator Should You Use? — NanoID generates shorter, URL-safe unique IDs using a custom alphabet. UUID v4 i…
- UUID Generator Online — Generate UUID v4 and v7 Instantly — A UUID is a 128-bit identifier formatted as 32 hex digits in 5 groups. UUID v4 u…
- UUID v4 Generator — Random UUIDs Explained — UUID v4 uses random bits to generate universally unique identifiers. Here's how …
Related tool
Generate UUID v4 and v7 identifiers in bulk.
Written by Mian Ali Khalid. Part of the Dev Productivity pillar.