Multi-tenant SaaS applications built on Rails face a recurring challenge: ensuring strict data isolation between tenants while maintaining developer productivity. Traditional solutions often introduce complexity, from fragile tenancy scoping to unreliable context preservation in background jobs. A new open-source gem called rails-tenantify addresses these pain points with a modern, row-level approach that integrates seamlessly with Rails 7+ and PostgreSQL.
Solving the core tenancy dilemma
Most SaaS products don’t need separate databases or schemas per tenant. Instead, they require reliable row-level isolation where every query automatically filters by tenant ID. The challenge is implementing this safely in production environments where mistakes can lead to catastrophic data leaks.
Common pitfalls include:
- Losing tenant context during background job retries or Sidekiq execution
- Accidentally executing bulk operations like
update_allordelete_allacross all tenants - Complex schema management when using tools like Apartment
- Fragile tenancy scoping that breaks with Rails 7+ updates
The new rails-tenantify gem was created specifically to address these gaps while providing a clean, maintainable API for modern Rails applications.
How rails-tenantify works
The gem implements row-level multi-tenancy through a simple yet powerful pattern. Developers include a module in their models and configure tenant resolution at the controller level.
class Project < ApplicationRecord
include Tenantify::Scoped
belongs_to_tenant :organization
endTenant context is set once per request, typically through subdomains or API headers:
class ApplicationController < ActionController::Base
set_tenant_by :subdomain
endThis approach automatically scopes all queries to the current tenant:
Tenantify.current_tenant = current_organization
Project.all # Returns only projects for the current tenant
Project.create!(name: "Q2 Roadmap") # Automatically sets organization_idThe gem also provides safe ways to temporarily switch contexts for administrative tasks:
# Temporarily switch to another organization
Tenantify.switch_to(other_org) do
Project.count # Scoped to the other organization
end
# Explicit bypass for maintenance operations
Tenantify.without_tenant do
Project.delete_all
endA modern alternative to legacy gems
While acts_as_tenant pioneered row-level multi-tenancy, it faces limitations in modern Rails environments, particularly around background job context preservation and bulk operation safety. rails-tenantify was rebuilt from the ground up for Rails 7+ with these challenges in mind.
Key improvements over legacy solutions include:
- Automatic tenant ID preservation in Sidekiq job payloads
- Strict protection against accidental bulk operations (
update_all,delete_all) - Built-in cross-tenant association validation
- Configurable tenant override auditing
- Native test helpers for reliable test suites
The gem also stands apart from schema-based solutions like Apartment, which require separate databases or schemas per tenant. rails-tenantify maintains simplicity by using a single database with row-level isolation, reducing DevOps overhead while providing the isolation most B2B SaaS products actually need.
Practical implementations and features
The gem offers flexible tenant resolution strategies beyond subdomains, including API headers for mobile or JSON clients:
# Configure tenant resolution by header
set_tenant_by :header, header: "X-Tenant-ID"
# Manual resolution based on session
before_action do
Tenantify.current_tenant = current_user.organization if user_signed_in?
endBackground job safety is built into the core design. The tenant context automatically survives enqueuing and execution:
class ExportJob < ApplicationJob
def perform
Tenantify.current_tenant # Automatically set to the originating organization
Project.find_each { |p| p.update!(exported: true) }
end
endFor Sidekiq users, middleware automatically injects tenant IDs into job payloads, ensuring context preservation even during retries.
The gem also implements strict protection against bulk operation mistakes:
Project.update_all(status: "archived") # Raises Tenantify::TenantMismatchError
# Explicit bypass for valid use cases
Tenantify.without_tenant do
Project.update_all(status: "migrated")
endCross-tenant association checks prevent accidental data leakage through relationships:
task.project = project_from_other_org
task.valid? # => false with validation errorsGetting started with rails-tenantify
Installation is straightforward. Add the gem to your Gemfile:
gem "rails-tenantify", "~> 0.1.2", require: "rails-tenantify"
bundle installCreate an initializer to configure tenant behavior:
config/initializers/tenantify.rb
Tenantify.configure do |config|
config.tenant_model = "Organization"
config.on_tenant_not_found = :raise
config.audit_overrides = :log
endThe gem expects a tenant_id column in your tenant model (e.g., organizations). Migrations follow standard Rails patterns, avoiding the complexity of schema-based solutions.
With rails-tenantify, Rails 7+ developers can implement robust, maintainable multi-tenancy without compromising safety or developer experience—letting them focus on building features rather than managing data isolation complexity.
As SaaS applications continue to grow in complexity, tools like rails-tenantify represent an important evolution in the Rails ecosystem, providing the reliability needed for production applications while keeping the development process simple and maintainable.
AI summary
Rails 7+ projelerinizde çoklu kiracılı sistemleri güvenle uygulamak için rails-tenantify gem'ini keşfedin. Satır düzeyinde kiracı izolasyonu, arka plan görevlerinde güvenlik ve toplu işlem koruması sunuyor.