Quick recommendation: For the safest, standards-based choice, use
GUID / UUID v4 (fully random) or
GUID / UUID v7 (time-ordered for databases). Consider
ULID only when you explicitly need its Base32 encoding and your ecosystem supports it.
Universally Unique Lexicographically Sortable Identifier (ULID) - github.com
Overview
Cloud platforms like AWS typically do not introduce their own GUID / UUID versions. Instead, they use standard IDs like ARNs and service-specific IDs, and sometimes standard GUIDs / UUIDs (often v4) for request/event IDs.
When choosing an identifier for your application or database schema, you're usually deciding between standard GUIDs / UUIDs (governed by RFC 9562) and ULID, a non-standard but popular GUID/UUID-like format.
Comparison table
| Property | GUID / UUID v4 | GUID / UUID v7 | ULID |
|---|
| Size | 128-bit | 128-bit | 128-bit |
|---|
| Encoding | Hex (8-4-4-4-12) | Hex (8-4-4-4-12) | Base32 (26 chars) |
|---|
| Time-sortable | No | Yes (millisecond) | Yes (millisecond) |
|---|
| Standard | RFC 4122 and RFC 9562 | RFC 4122 and RFC 9562 | No RFC |
|---|
| Human-readable | Moderate Hex with hyphens | Moderate Hex with hyphens | Good Base32, case-insensitive |
|---|
| Database-friendly | Good Native UUID support, but random | Excellent Native support + sortable | Good Often stored as text/binary |
|---|
| Collision resistant | Excellent 122 bits random | Excellent 74 bits random | Excellent 80 bits random |
|---|
| Best for | General-purpose IDs, privacy-friendly identifiers (no embedded data) and fully random | Database primary keys, event ordering, time-correlated IDs | Human-friendly sortable IDs, systems already using ULIDs |
|---|
Detailed comparison
Structure: 128 bits with 122 bits of randomness. No timestamp information.
- Pros: Universally supported, RFC standard, no time leakage and simple generation
- Cons: Random insertion pattern can cause index fragmentation in some databases
- Use when: You want maximum compatibility and don't need time-ordering
Example: 550e8400-e29b-41d4-a716-446655440000
Structure: 128 bits with 48-bit Unix millisecond timestamp prefix + 74 bits randomness.
- Pros: RFC standard, time-ordered, database-friendly and widely supported
- Cons: Reveals creation timestamp (millisecond precision)
- Use when: You want time-ordering with RFC compliance for database primary keys
Example: 018f3f5e-1c2d-7a9b-8f10-3c4d5e6f7a8b
ULID (Universally Unique Lexicographically Sortable Identifier)
Structure: 128 bits with 48-bit Unix millisecond timestamp + 80 bits randomness, encoded in Base32.
- Pros: Compact representation (26 chars), case-insensitive, time-ordered and URL-safe
- Cons: Not RFC GUID / UUID standard, requires library support and less universal than a GUID / UUID
- Use when: You need human-friendly, sortable IDs and your stack supports ULID
Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV
Which one should you use?
- Choose GUID / UUID v4 if you want the safest default: widely supported, easy to store, no embedded metadata and simple generation.
- Choose GUID / UUID v7 if you want a standards-based ID that is time-ordered and typically behaves better as a database primary key than random GUIDs / UUIDs.
- Choose ULID if you want time-sortable IDs and prefer a compact, human-friendly Base32 string representation and your stack already supports it.
Practical guidance for databases
If your main concern is database performance and index locality, you usually want identifiers that don't insert randomly across the index. That's the most common reason developers compare GUID / UUID v7 and ULID.
- Most compatible: GUID / UUID v4 (works everywhere, but can be random for indexes)
- Best "standard + sortable" combo: GUID / UUID v7 (time-ordered while staying a real GUID / UUID)
- Non-standard but popular: ULID (sortable, but may require extra tooling and conventions)
Performance considerations
- B-tree indexes: Time-ordered IDs (v7, ULID) reduce page splits and improve insert performance in databases using B-tree indexes (PostgreSQL, MySQL, etc.).
- LSM-tree databases: Random IDs (v4) may perform better in some LSM-tree databases (Cassandra, ScyllaDB), but this depends on partitioning strategy.
- Storage overhead: Both UUIDs and ULIDs are 128 bits (16 bytes), making storage comparable.
Interoperability and ecosystem support
GUID / UUID has the broadest support across programming languages, databases, and frameworks:
- Databases: Native GUID / UUID types in PostgreSQL, MySQL, SQL Server, Oracle, etc.
- Languages: Built-in support in Java, C#, Python, Go, Ruby, JavaScript, and many more
- APIs: JSON, REST, GraphQL commonly use GUID / UUID for resource identifiers
- ORMs: Entity Framework, Hibernate, Django ORM, and many others natively handle GUIDs / UUIDs
ULID requires library support and custom handling in many systems:
- Often stored as strings or binary blobs in databases
- Requires third-party libraries for generation and parsing
- May need custom converters for ORMs and serialization frameworks
Other identifier formats
Beyond GUIDs / UUIDs and ULID, 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 most applications requiring broad compatibility and standards compliance, standard GUIDs / UUIDs remain the recommended choice.
Warnings and trade-offs
- Identifiers are not secrets: GUID / UUID v4 / v7 and ULID are identifiers, not security tokens. Do not use them as access tokens or authentication secrets.
- Sorting depends on representation: "time-sortable" usually means the string form sorts by time, but database storage/byte-order can change behavior (varies by database/driver).
- Non-standard formats: ULID is not an RFC UUID. Some APIs, ORMs, and databases assume GUID / UUID formatting and won't accept it without custom handling.
- Time leakage: time-ordered IDs can reveal approximate creation time. If that matters, prefer GUID / UUID v4 or treat IDs as private/internal.
- Interoperability matters: if you work across many systems or languages, GUIDs / UUIDs typically win due to universal support.
- 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. ULID is a GUID/UUID-like identifier (128-bit) with a different text encoding (Base32 instead of hex) and different standardization. It's not governed by
RFC 9562. If you need a standards-based GUID / UUID, prefer
v4 or
v7.
A common modern choice is
GUID / UUID v7 because it remains a real GUID / UUID while offering time-ordered behavior, which reduces index fragmentation in B-tree databases.
GUID / UUID v4 is the simplest universal option, but can cause more random insert patterns in some index designs. ULID offers similar benefits to v7 but sacrifices
RFC compliance.
AWS primarily uses
service-specific identifiers (like ARNs, instance IDs, etc.) rather than standard GUIDs / UUIDs. Some AWS services emit standard GUIDs / UUIDs (often
v4) for request IDs or event IDs (e.g., X-Amzn-RequestId headers). But AWS does not define a custom GUID / UUID version or use ULID in public APIs.
Not directly. While GUID / UUID and ULID are both 128 bits, their internal structures differ significantly (different timestamp encodings, different random bit layouts). You can store them as equivalent binary blobs in databases, but you cannot meaningfully convert one format to another without losing semantic information. Choose one format and stick with it for consistency.
ULID is often considered more human-readable due to Base32 encoding (all uppercase, no special characters, case-insensitive). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV. GUIDs / UUIDs use hex with hyphens: 550e8400-e29b-41d4-a716-446655440000. For most purposes, GUIDs / UUIDs are "readable enough" and offer better ecosystem support.
Extremely low, similar to GUIDs / UUIDs. ULID has 80 bits of randomness per millisecond, providing sufficient entropy to make collisions negligible in practical scenarios. However, ensure your random number generator is cryptographically secure. Poor implementations can increase collision risk.
Possible with conversion. Since ULID is 128 bits, you can convert it to a UUID binary representation and store it in PostgreSQL's
uuid type, but you'll lose the Base32 string representation. You may need to store ULID as
text or
bytea to preserve the original encoding. For native PostgreSQL support and broad compatibility, use standard
GUIDs / UUIDs.
GUID / UUID v7 is an excellent choice for microservices. It provides time-ordering (useful for tracing and debugging),
RFC compliance (works with standard libraries), and decentralized generation (no coordination needed). If you need correlation IDs for distributed tracing, v7's timestamp prefix makes it easy to correlate events chronologically across services.
Conclusion
For broad interoperability and standards compliance, choose a standard GUID / UUID: v4 for general-purpose fully random IDs or v7 for time-ordered database-friendly identifiers.
Choose ULID when your ecosystem already uses it, when you need its specific Base32 encoding benefits or when its operational trade-offs are a deliberate fit for your system. However, be aware of the interoperability costs and library support requirements.