RLS Multi-Tenant
A Rails gem that provides PostgreSQL Row Level Security (RLS) based multi-tenancy for Rails applications.
📚 Learn more about PostgreSQL Row Level Security: PostgreSQL RLS Documentation
⚠️ IMPORTANT: Database Security Setup for RLS
This gem relies on PostgreSQL Row-Level Security (RLS) for tenant isolation. You MUST create a dedicated database role with proper RLS permissions before using this gem in production.
Features
- 🔒 Row Level Security: Automatic tenant isolation using PostgreSQL RLS
- 🛡️ Security Validation: Prevents running with privileged database users
- 🔄 Context Switching: Easy tenant context management
- 📦 Auto-inclusion: Automatic model configuration
- 🚀 Generators: Rails generators for quick setup
- ⚙️ Configurable: Flexible configuration options
- 🌐 Subdomain Middleware: Automatic tenant switching based on subdomain
Create Application Database Role
Create a dedicated role for your Rails application:
CREATE ROLE app_user
  WITH LOGIN
       CREATEDB          -- can create databases
       CREATEROLE        -- can create/modify other roles (except superuser)
       NOINHERIT         -- does not inherit privileges from roles it belongs to
       NOREPLICATION     -- cannot use replication
       NOBYPASSRLS       -- cannot bypass Row-Level Security
       NOSUPERUSER       -- is not a superuser
       PASSWORD 'strong_password';If you don't have a database created, you can create one with the new role:
CREATE DATABASE your_db_name OWNER app_user;If you already have a database created, make sure to grant ownership of the database to the new role:
ALTER DATABASE your_db_name OWNER TO app_user;Why This Role Configuration is Critical for RLS
- 
NOBYPASSRLS: ESSENTIAL for RLS security - prevents bypassing Row-Level Security policies that enforce tenant isolation
- 
NOSUPERUSER: Prevents superuser privileges that could compromise RLS policies
- 
LOGIN: Allows the role to connect to the database
- 
CREATEDB: Enables database creation for development/testing environments
- 
CREATEROLE: Allows creating other roles for application-specific users
- 
NOINHERIT: Ensures the role does not inherit privileges from parent roles
- 
NOREPLICATION: Prevents the role from being used for replication (security)
Without NOBYPASSRLS, Row-Level Security policies can be bypassed, completely breaking tenant isolation and exposing data across tenants.
Update Database Configuration
Update your config/database.yml to use the new role:
Installation
Add this line to your application's Gemfile:
gem 'rls_multi_tenant'And then execute:
bundle installQuick Start
- 
Install the gem configuration: rails generate rls_multi_tenant:install 
- 
Configure the gem settings: Edit config/initializers/rls_multi_tenant.rbto customize your tenant model:RlsMultiTenant.configure do |config| config.tenant_class_name = "Tenant" # Your tenant model class (e.g., "Organization", "Company") config.tenant_id_column = :tenant_id # Tenant ID column name config.enable_security_validation = true # Enable security checks (prevents running with superuser privileges) config.enable_subdomain_middleware = true # Enable subdomain-based tenant switching (default: true) config.subdomain_field = :subdomain # Field to use for subdomain matching (default: :subdomain) end 
- 
Setup the tenant model and migrations: rails generate rls_multi_tenant:setup 
- 
Run migrations: rails db:migrate 
Usage
Basic Multi-Tenant Models
Create a new model:
rails generate rls_multi_tenant:model User name emailYour models automatically include the MultiTenant concern:
class User < ApplicationRecord
  # Automatically includes MultiTenant concern
  include RlsMultiTenant::Concerns::MultiTenant
endTenant Context Switching
# Create a new tenant with subdomain
tenant = Tenant.create!(name: "Company A", subdomain: "company-a")# Switch tenant context for a block
Tenant.switch(tenant) do
  User.create!(name: "User from Company A", email: "user@company-a.com") # Automatically assigned to current tenant
end
# Switch tenant context permanently
Tenant.switch!(tenant)
User.create!(name: "User from Company A", email: "user@company-a.com")
Tenant.reset! # Reset context
# Get current tenant
current_tenant = Tenant.currentAutomatic Subdomain-Based Tenant Switching
The gem includes middleware that automatically switches tenants based on the request subdomain. This is enabled by default and works seamlessly with your tenant model.
The middleware automatically:
- Extracts the subdomain from the request host
- Finds the matching tenant by the subdomain field
- Switches the tenant context for the duration of the request
- Resets the context after the request completes
Usage:
# Create tenants with subdomains
tenant1 = Tenant.create!(name: "Company A", subdomain: "company-a")
tenant2 = Tenant.create!(name: "Company B", subdomain: "company-b")
# Users visiting company-a.yourdomain.com will automatically be in tenant1's context
# Users visiting company-b.yourdomain.com will automatically be in tenant2's context
# Users visiting yourdomain.com (no subdomain) will have no tenant contextPublic Access (Non-Tenanted Models)
Models that don't include RlsMultiTenant::Concerns::TenantContext are automatically treated as public models and can be accessed without tenant context. This provides a secure, explicit way to separate tenant-specific and public models.
Example:
# Public models (no tenant association)
class PublicPost < ApplicationRecord
  # No TenantContext concern included
  # These models are accessible without tenant context
end
# Tenant-specific models (automatically generated)
class User < ApplicationRecord
  # Automatically includes MultiTenant concern
  # These models require tenant context and are constrained by RLS
  include RlsMultiTenant::Concerns::MultiTenant
endSecurity Benefits:
- 
Explicit Intent: Models must explicitly include TenantContextto be tenant-constrained
- Fail-Safe: Public models are clearly separated from tenant models
- No Configuration Drift: Can't accidentally expose tenant data through misconfiguration
Requirements
- Rails 6.0+
- PostgreSQL 9.5+ (with UUID extension support)
- Ruby 2.7+
UUID Support
This gem uses UUIDs for the tenant model by default to ensure proper multi-tenant isolation. The enable_uuid migration must be run before creating tenant tables.
Contributing
- Fork the repository
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a Pull Request
License
The gem is available as open source under the terms of the MIT License.