Quick recommendation: For
RFC standards compliance, prefer
GUID / UUID v4 (fully random) or
GUID / UUID v7 (time-ordered). If you need a non-RFC alternative, choose
ULID for millisecond precision and 128-bit size (same as UUID), or
KSUID for more randomness (128 bits) and larger ID space (160-bit).
Universally Unique Lexicographically Sortable Identifier (ULID) - github.com
K-Sortable Unique Identifier (KSUID) - github.com
Overview
ULID (Universally Unique Lexicographically Sortable Identifier) is a 128-bit identifier using Crockford Base32 encoding (26 characters). It provides millisecond-precision timestamps with 80 bits of randomness. ULID is the same size as standard UUIDs, making it a drop-in replacement for storage purposes.
KSUID (K-Sortable Unique Identifier) is a 160-bit identifier created by Segment, using Base62 encoding (27 characters). It uses second-precision timestamps with 128 bits of randomness, providing a larger ID space than both UUIDs and ULIDs.
Neither format is governed by an RFC standard. Both are non-standard alternatives designed for specific use cases where time-ordering and sortability are priorities.
Comparison table
| Property | ULID | KSUID |
|---|
| Size | 128-bit (16 bytes) | 160-bit (20 bytes) |
|---|
| Encoding | Crockford Base32 26 chars | Base62 27 chars |
|---|
| Time-sortable | Yes Millisecond | Yes Second |
|---|
| Standard | Community spec No RFC | Segment.io spec No RFC |
|---|
| Human-readable | Good Base32, case-insensitive | Good Base62, compact |
|---|
| Database-friendly | Moderate No native type (only text/binary), 16 bytes | Moderate No native type (only text/binary), 20 bytes |
|---|
| Collision resistant | Excellent 80 bits random | Excellent 128 bits random |
|---|
| Best for | Human-friendly sortable IDs, URL-safe identifiers | Log/event systems, large-scale distributed tracing |
|---|
Detailed comparison
ULID (Universally Unique Lexicographically Sortable Identifier)
Structure: 128 bits with 48-bit Unix millisecond timestamp + 80 bits randomness, encoded in Crockford Base32.
- Pros: Millisecond precision, compact Crockford Base32 encoding (26 chars), 128-bit size (same as UUID), lexicographically sortable, no ambiguous characters (0/O, 1/I/L)
- Cons: Not RFC standard, 80 bits randomness (less than KSUID), requires library support, no native database type
- Use when: You need time-sortable IDs with millisecond precision and want UUID-compatible storage size
Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV
KSUID (K-Sortable Unique Identifier)
Structure: 160 bits with 32-bit Unix second timestamp + 128 bits randomness, encoded in Base62.
- Pros: Larger ID space (160 bits), 128 bits of randomness (excellent collision resistance), time-ordered, URL-safe Base62 encoding, second-precision timestamps
- Cons: Not RFC standard, larger storage than UUIDs/ULIDs (20 bytes vs 16 bytes), requires library support, less universal than UUIDs
- Use when: You need maximum entropy with time-ordering and second-precision is sufficient
Example: 0ujsszwN8NRY24YaXiTIE2VWDTS
Which one should you use?
- Choose ULID if you need millisecond-precision timestamps, want 128-bit size (same storage as UUID), prefer Crockford Base32 encoding (no ambiguous characters), or need lexicographically sortable IDs.
- Choose KSUID if you need a larger ID space (160 bits), require more randomness (128 bits vs 80 bits), second-precision timestamps are sufficient, or your infrastructure already uses KSUID.
- Consider GUID / UUID v7 if RFC standards compliance matters to you. It offers time-ordering with millisecond precision while being a real UUID with broad ecosystem support.
Practical guidance for databases
Both ULID and KSUID are time-ordered identifiers that improve database performance compared to random IDs. When records are inserted in approximate time order, B-tree indexes experience fewer page splits and better cache locality.
- Storage size: ULID uses 16 bytes (same as UUID), while KSUID uses 20 bytes. At scale, this 25% difference can add up significantly.
- Index fragmentation: Both formats reduce fragmentation compared to random UUIDs since records are inserted in approximate time order.
- Native type support: Neither ULID nor KSUID has native database type support. Both must be stored as strings or binary blobs.
Performance considerations
- B-tree indexes: Both ULID and KSUID reduce page splits and improve insert performance in databases using B-tree indexes (PostgreSQL, MySQL, etc.).
- Storage overhead: ULIDs are 128 bits (16 bytes), KSUIDs are 160 bits (20 bytes). Consider this difference when choosing at scale.
- Timestamp precision: ULIDs provide millisecond ordering, KSUIDs provide second ordering. IDs created within the same second/millisecond are ordered by their random component.
Interoperability and ecosystem support
Both ULID and KSUID are non-RFC formats that require library support. Neither has the universal ecosystem support that standard UUIDs enjoy.
ULID ecosystem:
- Broader adoption than KSUID in many ecosystems (JavaScript, Go, Rust, Python)
- 128-bit size allows storage in UUID-sized database columns (as binary)
- Crockford Base32 encoding is case-insensitive and avoids ambiguous characters
- Must be stored as strings or binary in databases (no native ULID type)
KSUID ecosystem:
- Originally developed by Segment, well-supported in Go ecosystem
- Requires custom handling in most databases and ORMs
- Must be stored as strings (27 chars) or binary (20 bytes)
- May need custom converters for serialization frameworks
Other identifier formats
Beyond ULID and KSUID, there are many other identifier formats each with their own trade-offs. For a comprehensive comparison of all popular identifier formats, see our complete identifier comparison guide.
For applications requiring broad compatibility and standards compliance, standard GUIDs / UUIDs (particularly GUID / UUID v7) remain an excellent choice that combines time-ordering with RFC compliance.
Warnings and trade-offs
- Identifiers are not secrets: Both ULID and KSUID are identifiers, not security tokens. Do not use them as access tokens or authentication secrets.
- Time leakage: Both ULID and KSUID embed timestamps that reveal approximate creation time. If timestamp privacy matters, consider GUID / UUID v4 instead.
- Non-standard formats: Neither ULID nor KSUID is an RFC standard. Some APIs, ORMs, and databases assume UUID formatting and won't accept them without custom handling.
- Collision risk differences: ULID has 80 bits of randomness while KSUID has 128 bits. At extremely high generation rates, ULID has higher collision probability.
- Sorting depends on representation: "Time-sortable" usually means the string form sorts by time, but database storage/byte-order can change behavior.
- Migration complexity: Switching from one identifier format to another in an existing system can be challenging and may require data migration.
Frequently Asked Questions
No. While both are time-sortable alternatives to UUID, they differ significantly: ULID is 128-bit with Crockford Base32 encoding (26 chars) and millisecond timestamp precision, while KSUID is 160-bit with Base62 encoding (27 chars) and second timestamp precision. ULID has 80 bits of randomness vs KSUID's 128 bits.
KSUID uses 128 bits of randomness compared to ULID's 80 bits. Combined with the 32-bit timestamp, this results in 160 bits total. The extra randomness provides higher collision resistance for extremely high-volume systems, though ULID's 80 bits is sufficient for most applications.
Both work well as primary keys since they're time-ordered.
ULID is preferred when storage size matters (16 bytes vs 20 bytes) and millisecond precision is needed.
KSUID is better when you need maximum collision resistance. For standards compliance, consider
GUID / UUID v7 which offers similar benefits with RFC support.
No. ULID is 128 bits while KSUID is 160 bits, and their internal structures are completely different (different timestamp precision, different randomness bits, different encoding). You cannot meaningfully convert between them. Choose one format for your system and stick with it.
Both are designed for human-readability. ULID uses Crockford Base32 which avoids ambiguous characters (0/O, 1/I/L): 01ARZ3NDEKTSV4RRFFQ69G5FAV. KSUID uses Base62 (alphanumeric only): 0ujsszwN8NRY24YaXiTIE2VWDTS. Both are more readable than hex-encoded UUIDs.
ULID has slightly higher collision risk due to 80 bits of randomness vs KSUID's 128 bits. However, 80 bits is still sufficient for most practical applications. At extremely high generation rates (millions per millisecond), KSUID's extra randomness provides better protection. Both require cryptographically secure random number generation.
Neither directly. PostgreSQL's
uuid type is exactly 128 bits with specific hex formatting. ULID (128-bit but Base32) must be stored as
text (26 chars) or
bytea (16 bytes). KSUID (160-bit) must be stored as
text (27 chars) or
bytea (20 bytes). For native PostgreSQL UUID support, use standard
GUIDs / UUIDs.
Both work well for logging. Choose
ULID if you need
millisecond-precision timestamps for fine-grained event ordering. Choose
KSUID if second-precision is sufficient and you want maximum entropy. For standards compliance with time-ordering, consider
UUID v7.
Choose KSUID when: (1) you need maximum collision resistance (128 bits vs 80 bits random), (2) second-precision timestamps are sufficient, (3) your infrastructure already uses KSUID (especially Go ecosystems), or (4) the extra 4 bytes of storage isn't a concern. Choose ULID for millisecond precision, smaller storage, or Crockford Base32 encoding preferences.
Conclusion
Choose ULID when you need millisecond-precision timestamps, prefer the same 128-bit storage size as UUIDs, want Crockford Base32 encoding (no ambiguous characters), or need lexicographically sortable identifiers with compact representation.
Choose KSUID when you need maximum collision resistance (128 bits of randomness), second-precision is sufficient for your use case, or your infrastructure already uses KSUID (common in Go ecosystems).
For RFC standards compliance with time-ordering benefits, consider GUID / UUID v7 as an alternative that provides millisecond precision with broad ecosystem support.