UUID v4 is stateless and unpredictable, which makes it safe for external identifiers but useless for understanding the sequence of events in a distributed system. UUID v7’s millisecond timestamp changes that — you can read when a record was created, sort by creation time across services, and correlate events without a shared sequence counter. This article covers the practical implications for distributed architectures.

What the timestamp gives you

A UUID v7 encodes a 48-bit Unix millisecond timestamp in its leading bits. This means:

Event ordering across services

In a monolith, database sequence numbers give you global event order. In a distributed system with multiple services each writing to their own database, there is no global sequence. UUID v7 provides approximate ordering — accurate to the millisecond, with sub-millisecond ordering within a single node via the monotonic counter.

Service A (node 1):  019236a7-b4f2-7000-...  (ts: 1727308800000 ms)
Service B (node 2):  019236a7-b4f4-7000-...  (ts: 1727308800002 ms)
Service A (node 1):  019236a7-b4f5-7000-...  (ts: 1727308800003 ms)

Sorting these three UUIDs lexicographically gives the correct cross-service event sequence. You can reconstruct a timeline of events across services purely from their IDs — no centralised sequence, no distributed transaction.

Distributed tracing and log correlation

Trace IDs and span IDs in distributed tracing systems (OpenTelemetry, Jaeger, Zipkin) are typically 128-bit or 64-bit random values. Replacing them with UUID v7 values adds a timestamp you can use to:

// OpenTelemetry custom ID generator using UUID v7
import { v7 as uuidv7 } from 'uuid';

const traceId = uuidv7().replace(/-/g, ''); // 32-char hex, 128-bit
const spanId  = uuidv7().replace(/-/g, '').slice(0, 16); // 16-char hex, 64-bit

The trace ID now encodes its own start time, making it self-describing.

Multi-node ID generation without coordination

The traditional alternatives to UUID v4 for distributed IDs are:

ApproachCoordination neededSortableCollision-safe
Auto-incrementYes — central DB sequenceYesYes
Snowflake IDYes — node ID assignmentYesYes
UUID v4NoNoYes
UUID v7NoApproximate (ms)Yes
ULIDNoApproximate (ms)Yes

UUID v7 sits in the same column as ULID: no coordination, millisecond-precision ordering, collision-safe. The difference is that UUID v7 is an IETF standard with native database type support, while ULID is a community spec with no native type.

Snowflake IDs (used by Twitter/X, Discord, and others) provide stricter ordering guarantees because they embed a node ID and a per-node sequence counter, but they require each node to be assigned a unique ID at startup — a coordination step UUID v7 avoids entirely.

Clock skew and monotonicity

Each node generating UUID v7 values uses its local system clock. If two nodes have different clock values — common in VM environments where clocks drift — their UUIDs will not interleave in strict wall-clock order. Node A at t=1000ms and Node B at t=998ms will produce UUIDs that sort with A before B, even if B’s event “happened” first in real time.

This is acceptable for most use cases. If you need strict global ordering, you need a coordination layer regardless of ID format — no decentralised ID scheme can provide it without synchronised clocks (GPS time, PTP) or a sequencer service.

Within a single node, UUID v7 is strictly monotonic: the RFC 9562 §6.2 Method 1 counter ensures that two UUIDs generated in the same millisecond sort in generation order. A node’s own sequence is always correct.

Practical guidance

Use UUID v7 for:

Keep UUID v4 for:

Extracting the timestamp in practice

// JavaScript
function v7Timestamp(uuid) {
  return parseInt(uuid.replace(/-/g, '').slice(0, 12), 16);
}
const ms = v7Timestamp('019236a7-b4f2-7000-8d3e-9c1a2b3d4e5f');
console.log(new Date(ms).toISOString()); // 2026-09-25T16:00:00.000Z
# Python
import uuid as _uuid
from datetime import datetime, timezone

def v7_timestamp(u: str) -> datetime:
    ms = int(u.replace('-', '')[:12], 16)
    return datetime.fromtimestamp(ms / 1000, tz=timezone.utc)
// Go
import "github.com/google/uuid"

u, _ := uuid.Parse("019236a7-b4f2-7000-8d3e-9c1a2b3d4e5f")
ts, _ := u.Time()  // returns time.Time for v7

Further reading

Frequently asked questions

Can UUID v7 replace a central sequence counter in distributed systems?

For most use cases, yes. UUID v7's millisecond timestamp gives approximate cross-node ordering without any coordination. Each node generates independently, and UUIDs sort in creation order to millisecond precision. For strict global ordering you still need clock synchronisation or a coordination layer — but UUID v7 handles the 99% case where millisecond-level ordering is sufficient.

Does UUID v7 leak sensitive information?

Yes — the first 48 bits are a Unix millisecond timestamp. Anyone with a v7 UUID can read when the record was created to the millisecond. Two v7 UUIDs also reveal how many records were created between them. Use v4 for externally visible identifiers where creation time or volume is sensitive.

Should I use v4 or v7?

Use v7 for database primary keys (time-sortable, index-friendly) and v4 for anything where creation order could leak information, like tokens or share links.

Can two nodes generate the same UUID v7?

Collision is astronomically unlikely. UUID v7 has 74 bits of randomness (62 in rand_b plus 12 in rand_a when not using a counter). Two nodes generating a UUID in the same millisecond would need those 74 random bits to match — a probability of 1 in 2⁷⁴ (about 1 in 18 quadrillion) per pair of values.

Does UUID v7 require clock synchronisation across nodes?

No coordination is required — each node generates independently. Clock skew between nodes means their UUIDs will not interleave perfectly in time order, but each node's own UUIDs will be monotonically increasing. For globally strict ordering across nodes you need a coordination layer (logical clocks, a sequencer service) regardless of which ID format you use.