Generating UUIDs in Ruby
On this page
Ruby has UUID v4 in the standard library via SecureRandom. For v7 and other versions, several small gems exist.
Quick start
require "securerandom"
SecureRandom.uuid
# => "0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b"
SecureRandom.uuid returns a string. Ruby has no built-in UUID type — strings are the convention. Generation uses the platform CSPRNG (/dev/urandom on Unix, BCryptGenRandom on Windows).
v7 with the uuidv7 gem
# Gemfile
gem "uuidv7"
require "uuidv7"
UUIDv7.generate
# => "01928a47-3b30-7c5e-9d1a-f0b8c4a7e923"
Tiny gem, no dependencies, ~50 lines of code. Generates v7 UUIDs with same-millisecond monotonic sequencing.
For other versions (v1, v3, v5), the uuid gem covers them, though it’s less actively maintained than its alternatives.
Rails / ActiveRecord
PostgreSQL:
# config/application.rb (Rails 7+)
config.generators do |g|
g.orm :active_record, primary_key_type: :uuid
end
class CreateUsers < ActiveRecord::Migration[7.2]
def change
create_table :users, id: :uuid do |t|
t.string :email, null: false, index: { unique: true }
t.timestamps
end
end
end
This uses Postgres’s gen_random_uuid() as the default — v4. For v7 in Rails:
create_table :users, id: false do |t|
t.uuid :id, primary_key: true, default: -> { "uuidv7()" } # PG 18+
t.string :email
end
Or generate in Ruby for cross-database compatibility:
class User < ApplicationRecord
before_create -> { self.id ||= UUIDv7.generate }
end
Sequel
DB.create_table :users do
primary_key :id, :uuid, default: Sequel.function(:gen_random_uuid)
String :email, null: false, unique: true
end
Same pattern as ActiveRecord — let the database generate the default, or do it in Ruby for v7.
JSON serialization
UUIDs are strings in Ruby, so they serialize to JSON strings without any special configuration:
require "json"
user = { id: SecureRandom.uuid, email: "alice@example.com" }
user.to_json
# => "{\"id\":\"0e6f1b8c-...\",\"email\":\"alice@example.com\"}"
Sidekiq job arguments
UUIDs in job arguments serialize via to_json. Sidekiq supports them out of the box:
class SendWelcomeEmail
include Sidekiq::Job
def perform(user_id)
user = User.find(user_id)
UserMailer.welcome(user).deliver_now
end
end
SendWelcomeEmail.perform_async(SecureRandom.uuid)
Validation
UUID_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
def valid_uuid?(s)
UUID_REGEX.match?(s)
end
For Rails parameter validation:
class UserController < ApplicationController
def show
@user = User.find(params[:id])
rescue ActiveRecord::RecordNotFound
head :not_found
end
end
ActiveRecord’s find raises on invalid UUID format too, so a 404 covers both “not found” and “malformed” gracefully.
Comparing UUIDs
UUIDs are strings, so == works:
a = SecureRandom.uuid
b = a.dup
a == b # => true
a.eql?(b) # => true
For case-insensitive comparison (rare — most generators emit lowercase):
a.downcase == b.downcase
Bytes for binary storage
require "securerandom"
uuid = SecureRandom.uuid # canonical
hex = uuid.delete("-") # 32 hex chars
bytes = [hex].pack("H*") # 16 raw bytes
For PostgreSQL with the pg gem, the native uuid type accepts strings directly — no manual byte conversion needed.
For MySQL with BINARY(16), convert in Ruby:
ActiveRecord::Base.connection.execute(
"INSERT INTO users (id, email) VALUES (UUID_TO_BIN(?), ?)",
uuid, email
)
Performance
SecureRandom.uuid is fine for typical web throughput (~500K UUIDs/sec on a modern Ruby). If you’re generating UUIDs in a tight loop, the securerandom gem from MRI is slower than native libraries — but it’s almost never a bottleneck.
For really high-throughput generation, drop into FFI with the C uuid library, or batch via SecureRandom.bytes(16 * count) and set version/variant bits manually.
Common pitfalls
- Don’t use
rand(...)to generate UUIDs. It’s not cryptographically random and not collision-resistant. - Postgres returns UUIDs as strings, not as a special type. Rails normalizes them to lowercase canonical form on read.
SecureRandom.uuidalways returns lowercase. If you compare with values from a Microsoft system that emits uppercase, normalize first.UUIDv7.generateis monotonic per process, not across processes. If two Sidekiq workers generate v7 UUIDs at the exact same millisecond, the global ordering may be slightly inconsistent — irrelevant for primary keys, but don’t rely on it for distributed event ordering.
Cheat sheet
| Goal | Code |
|---|---|
| Random UUID | SecureRandom.uuid |
| Time-ordered (v7) | UUIDv7.generate |
| Validate | UUID_REGEX.match?(s) |
| Rails id column | t.uuid :id, primary_key: true |
| Bytes for binary storage | [uuid.delete('-')].pack('H*') |
Try the tools
- UUID generator — generate v4, v7, etc. in browser
- UUID v7 generator — primary keys
- UUID validator — decode any UUID
- PostgreSQL UUID guide — Rails-friendly DB patterns