ULID generator
Generate sortable, timestamped identifiers — and decode the time back out of one.
What a ULID fixes about a UUID
A random UUID is excellent at being unique and terrible as a database key. Version 4 is random across its whole length, so consecutive inserts land in unrelated places in the index. On a B-tree that means the write hot spot is scattered across the whole structure rather than staying at one end, the index fragments, and the working set that has to stay in memory is the entire index rather than its most recent page.
A ULID puts a 48-bit millisecond timestamp in the high bits and eighty bits of randomness after it. It is still 128 bits and still practically collision-free, but identifiers created near each other in time sort near each other — so inserts append, and the index behaves the way an auto-incrementing integer does.
And it sorts as text
This is the underrated part. The encoding is Crockford base32, chosen so that lexicographic order of the string matches numeric order of the bytes. You can ORDER BY id on a text column and get chronological order, sort a list of filenames and get chronological order, and read a log sorted by identifier without a timestamp column at all.
Crockford base32 also drops I, L, O and U from the alphabet, so there is no ambiguity between one and I or zero and O when somebody reads an identifier over the phone, and no accidental profanity.
The same millisecond
Two ULIDs generated in the same millisecond have the same timestamp and independent random parts, so their relative order is arbitrary — which quietly defeats the point in exactly the situation where you generate identifiers in a tight loop. The monotonic option, on by default, increments the random component instead of redrawing it when the clock has not moved, so identifiers made in a burst still sort in creation order.
Decoding one
Paste a ULID into the decode field and you get back the moment it was created, to the millisecond. That is genuinely useful in support work: given only an identifier from a log line or a URL, you can tell exactly when the record was made without looking anything up.
When not to use one
The timestamp is readable by anyone holding the identifier. If a ULID appears in a public URL, you have published when that record was created — which is usually harmless and occasionally is not, if creation time is competitively or personally sensitive. It is also not a secret: eighty bits of randomness is plenty against collision and it is not a security token. Use a random token where you need unguessability.
Common questions
How is a ULID different from a UUID?
A ULID starts with a millisecond timestamp, so identifiers sort chronologically and database inserts append rather than scattering across the index. A UUIDv4 is random throughout.
Are ULIDs unique?
Effectively. Eighty bits of randomness per millisecond makes a collision within the same millisecond vanishingly unlikely, and the monotonic mode removes it entirely within one generator.
Why does the alphabet skip I, L, O and U?
Crockford base32 excludes them so identifiers cannot be misread — no confusing 1 with I or 0 with O — and so random strings cannot spell anything unfortunate.
Can I get the creation time back out?
Yes. Paste one into the decode field and you get the exact millisecond it was generated. That works on any ULID, from anywhere.
Should I use a ULID in a public URL?
Be aware it reveals when the record was created. Usually that is fine; if creation time is sensitive, use an opaque random identifier instead.
What is the monotonic option for?
Without it, two ULIDs made in the same millisecond sort arbitrarily. With it, the random part increments so they still sort in the order you made them.