UUID v7’s key advantage over UUID v4 — the embedded millisecond timestamp — also introduces a dependency on the system clock. In distributed systems, clocks are never perfectly synchronized. Understanding the failure modes helps you design systems that tolerate clock imperfections without compromising correctness.
What Clock Skew Means for UUID v7
UUID v7 encodes the current Unix time in milliseconds in the high 48 bits:
018fbe3a-4c5d-7xxx-xxxx-xxxxxxxxxxxx
^^^^^^^^^^^^^^^^^
48-bit ms timestamp (big-endian)
If a generator node’s clock is ahead by 500ms compared to another node, its UUIDs will have larger timestamp prefixes — they’ll sort after UUIDs from the slower node even if they were generated before them. This is clock skew: a difference in time between nodes at a single moment.
Clock drift is the gradual divergence over time. Both can cause UUID v7 values from different nodes to be interleaved out of creation order when sorted.
Three Failure Scenarios
1. Multi-node generation with skewed clocks
Nodes A and B generate UUIDs simultaneously. Node A’s clock is 200ms ahead. UUIDs from node A sort after UUIDs from node B even though they were generated at the same real-world moment. For most applications (database primary keys, correlation IDs), this is acceptable — exact cross-node ordering is rarely required.
2. NTP clock correction (backward jump)
NTP can step the system clock backward when correcting significant drift. A generator running on a node that jumps back 50ms will produce UUIDs with smaller timestamp prefixes than the ones it just generated — breaking the monotonically-increasing property within that single node.
3. VM migration or container restart
When a virtual machine or container moves to a different host or resumes from a snapshot, the system clock may jump forward or backward depending on whether the host clock was synchronized. This is particularly relevant for Kubernetes pods and serverless functions.
RFC 9562 Mitigations
RFC 9562 §6.2 describes three methods for handling clock rollback:
Method 1: Freeze and increment (recommended)
When the current clock value is less than the last used timestamp, hold the timestamp at the last known value and increment the monotonic counter. The uuid npm package uses this approach — rapid successive calls or a minor clock rollback produce UUIDs that remain in order for up to 4,096 ms of rollback.
Method 2: Wait for the clock to catch up
Block UUID generation until SystemTime::now() exceeds the last used timestamp. Safe but can stall throughput in applications that need UUIDs at high rate.
Method 3: Re-seed the counter
When a backward clock jump is detected, generate a new random seed for the counter. Order is not preserved across the rollback boundary, but generation continues immediately.
Practical Recommendations
- Single-node applications: a monotonic counter handles minor NTP corrections transparently. No special action needed.
- Multi-node applications: don’t assume strict cross-node UUID ordering. Use
ORDER BY created_atfor user-visible sorting, notORDER BY id. UUID v7’s ordering is approximate, not exact, across nodes. - High-correctness event sourcing: if strict global ordering matters, use a logical clock (Lamport timestamp, vector clock) as the source of truth. UUID v7 is not a substitute for a distributed consensus protocol.
- Cloud deployments: enable hardware clock synchronization on VMs. Chrony or the platform NTP service keeps drift under 1ms in practice.
Related Resources
- RFC 9562 §6.2 — monotonic counter and clock rollback
- NTP Pool Project — public NTP servers for clock synchronization
- uuid npm package — implements monotonic counter with rollback protection
- Lamport, “Time, Clocks, and the Ordering of Events” — foundational paper on logical clocks
Frequently asked questions
What happens to UUID v7 when the system clock goes backwards?
RFC 9562 §6.2 defines three mitigations: (1) freeze and increment the monotonic counter until the clock catches up, (2) add a random increment to stay ahead of the last known timestamp, or (3) wait until the clock advances. Most production libraries (uuid npm, java-uuid-generator) use approach 2. PostgreSQL 18's uuidv7() does not implement a monotonic counter — rapid backward skew can produce non-monotonic values within a single session.
What is a UUID v7 monotonic counter?
A monotonic counter is a per-millisecond integer that some UUID v7 generators use to keep successive UUIDs in creation order even within the same millisecond. RFC 9562 §6.2 recommends it but does not require it. The uuid npm package implements a counter; Python 3.13's uuid.uuid7() does not.
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.
Does clock skew break UUID v7 uniqueness?
No — UUID v7 uniqueness comes from the 74 random bits, not the timestamp. Clock skew cannot cause collisions. What it can break is sort order: if a node's clock drifts backward (due to NTP correction or VM migration), newly generated UUIDs will have smaller timestamp prefixes than recently generated ones, violating chronological ordering. RFC 9562 §6.2 provides mitigation strategies.
How does RFC 9562 recommend handling clock rollback in UUID v7?
RFC 9562 §6.2 recommends three strategies: (1) freeze the timestamp and increment the monotonic counter until the clock catches up, (a monotonic counter prevents out-of-order values within a rollback window up to 4096 milliseconds); (2) wait until the system clock advances past the last used timestamp; (3) generate a new random counter seed when the clock moves backward. Libraries like the uuid npm package implement strategy 1 automatically.