When most people think of UUIDs, they think of chaos: random strings that can’t be sorted, predicted, or interpreted. That’s true — if you’re using UUIDv4.
But what if you want some order?
That’s where UUIDv1 comes in. It’s a time-based identifier that encodes creation time, enabling natural sort order and efficient querying for time-series data.
Let’s take a look at how UUIDv1 works, when to use it, and the trade-offs you should be aware of.
🧬 What Is UUIDv1?
UUIDv1 is defined in [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) and encodes:
- 60 bits of timestamp (in 100-nanosecond intervals since Oct 15, 1582)
- 48 bits of node ID (usually MAC address)
- 14 bits of clock sequence (to prevent duplication)
The layout ensures that UUIDs generated later will sort after those generated earlier — making UUIDv1 ideal for ordered data.
📦 Why Use UUIDv1?
UUIDv1 offers:
- Globally unique identifiers
- Natural chronological ordering
- No central coordination
- Fast generation
This makes it perfect for:
- Time-series logs
- Message queues
- Event sourcing
- Append-only databases
- Data replication where ordering matters
⚠️ Trade-Offs of UUIDv1
Despite its benefits, UUIDv1 has some drawbacks:
❌ Privacy Risk
- Encodes part of the MAC address
- Reveals creation timestamp — down to 100ns
❌ Predictability
- If you know the pattern, UUIDv1s are guessable in order
- Not suitable for public identifiers or security tokens
🧪 Generating UUIDv1 (Examples)
🔹 Python
import uuid
u = uuid.uuid1()
print(u)
Python’s uuid1()
uses your machine’s MAC address and system time. You can override the node for privacy:
uuid.uuid1(node=0x123456789abc)
🔹 Go (using github.com/gofrs/uuid)
package main
import (
"fmt"
"github.com/gofrs/uuid"
)
func main() {
id, _ := uuid.NewV1()
fmt.Println("UUIDv1:", id)
}
🔹 Java
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedGenerator;
public class UUIDv1Example {
public static void main(String[] args) {
TimeBasedGenerator gen = Generators.timeBasedGenerator();
System.out.println(gen.generate());
}
}
📅 UUIDv1 for Time-Series Databases
Because UUIDv1s sort chronologically, they’re a good fit for:
- Time-ordered tables (e.g., in PostgreSQL or MySQL)
- Index-friendly inserts (better than UUIDv4 for B-trees)
- Range queries based on time
PostgreSQL Tip
CREATE TABLE events (
id UUID PRIMARY KEY,
event_data JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Index for time-range scanning
CREATE INDEX ON events (id);
With UUIDv1, ORDER BY id DESC
roughly equals ORDER BY created_at DESC
.
🧰 Alternatives If UUIDv1 Isn't Right
If you're concerned about leaking timestamps or MAC addresses:
- UUIDv7: A newer spec that encodes timestamp + randomness without MAC
- ULID: Lexicographically sortable, base32-encoded timestamped ID
- Snowflake IDs: Used by Twitter, encodes timestamp + worker ID + sequence
> TL;DR: Use UUIDv1 for internal ordered IDs, but not for public-facing identifiers.
🔐 Security Considerations
- Don’t expose UUIDv1 in URLs or public APIs
- Be aware of info leaks from MAC address and creation time
- Prefer UUIDv4, UUIDv7, or secure tokens for anything involving authentication
Final Thoughts
UUIDv1 might be old, but it’s far from obsolete. If your system needs order, uniqueness, and performance — and you’re not worried about exposing timestamps — it’s still a solid choice.
Just use it wisely, and always understand the trade-offs before choosing an identifier format.
🕒 UUIDv1: Because sometimes, order really does matter.