How to Use UUIDv7 in Rails for Primary Keys

UUIDv7 is a new time-sorted UUID format. Here's how to use it in Rails (and Postgres).

Using UUIDs for primary keys offers many benefits, but there are some downsides to consider. The most widely-used UUIDv4 is fully random, which is ideal for minimizing the risk of collision. However, random IDs as primary keys do not index and sort efficiently, leading to index bloat and performance issues.

There is a new standard that addresses this problem by including a UNIX timestamp1 in the initial bits: UUIDv72.

10.times { puts UUID7.generate }

# 018c2b95-b764-7615-a924-cc5b910ed1e5
# 018c2b95-b764-713f-aff1-e2668182bc68
# 018c2b95-b764-7b43-bec8-3c204a04433a
# 018c2b95-b764-7521-b1de-0638499f2f75
# 018c2b95-b764-7380-895d-4416494f9a05
# 018c2b95-b764-7cb1-a91f-24e598a4bc36
# 018c2b95-b764-7218-a1d1-fa20a475d6b0
# 018c2b95-b764-7eab-9e08-d0b321a69240
# 018c2b95-b764-7d25-a033-845c81610339
# 018c2b95-b764-7143-af66-0a849883f9d0

How to set up UUIDv7 in Rails

As of this writing, selecting id: :uuid in Rails defaults to using UUIDv4 for primary keys, with the generation deferred to Postgres.

While Postgres does not currently offer a built-in function for generating UUIDv7, we can still use the uuid type to store values. One advantage of UUIDs over sequential IDs is that they can be generated anywhere — in this example, we’ll use our Rails backend.

First, let’s set up our Rails app to use UUID as the primary key. We’re going to enable pgcrypto. This isn’t strictly necessary, but Rails will set up gen_random_uuid() as the default when you set id: :uuid.

rails g migration EnablePgcrypto

Here’s the migration:

class EnablePgcrypto < ActiveRecord::Migration[7.1]
  def change
    enable_extension 'pgcrypto'
  end
end

Then, add the following to config/application.rb:

config.generators do |generate|
  generate.orm :active_record, primary_key_type: :uuid
end

From now on, Rails will use the UUID type for primary keys for new models created with rails generate model.

At this point, we’re all set up, but we’re still using UUIDv4. Let’s change that:

bundle add uuid7

To use UUIDv7 for our primary keys, we have to create them before they are saved to the database. This can be achieved with a before_create callback in the ApplicationRecord:

class ApplicationRecord < ActiveRecord::Base
  primary_abstract_class

  before_create :generate_uuid_v7

  private

    def generate_uuid_v7
      return if self.class.attribute_types['id'].type != :uuid

      self.id ||= UUID7.generate
    end
end

That’s it! From now on, all new records will have a UUIDv7 primary key, without affecting the existing tables that may use sequential (integer) IDs.

Further reading

Also, check out BasedUUID.


  1. The trade-off, compared to v4, is a higher risk of collision, so you’ll have to keep that in mind. Another potential downside is that UUIDv7 exposes the timestamp of record creation. ↩︎

  2. UUIDv7 is now recommended over v6↩︎

Published on (Updated: )