May 14, 2025
A few months ago, I had to build a SaaS platform for music instrument stores. Each store needed to manage its own products, customers, and sales, but everything had to run under one codebase.
I ended up going with the apartment gem to handle multi-tenancy â specifically, schema-based separation using PostgreSQL. It was a learning curve, but totally worth it. Hereâs how I made it work and some things I learned along the way.
Ready to Add Multi-Tenancy to Your Rails App?
Whether youâre building a SaaS platform or managing data for multiple clients, multi-tenancy is a game-changer â and weâve got the tools and experience to help you implement it the right way.
Letâs set up clean, isolated environments for each of your customers using proven solutions like the apartment gem.
The Setup
The idea was simple: every store that signs up gets its own schema. That way, their data stays isolated, and they donât accidentally see another storeâs inventory or customers.
I added apartment to the Gemfile:
gem 'apartment'
Then configured it like this:
# config/initializers/apartment.rb
Apartment.configure do |config|
config.excluded_models = %w[User] # Global models like admin users
config.use_schemas = true
end
# This switches tenants based on subdomain (e.g. store1.myapp.com)
Rails.application.config.middleware.use Apartment::Elevators::Subdomain
This setup assumes each store logs in via their own subdomain, like rockstore.myapp.com or jazzcenter.myapp.com.
Creating Stores (Tenants)
Once a store signs up, I just run:
Apartment::Tenant.create("rockstore")
That creates a new schema inside PostgreSQL. Then I seed it with default data or let them start fresh.
For dev and testing, I used something like this in the Rails console:
Apartment::Tenant.switch("rockstore") do
Instrument.create(name: "Fender Jazz Bass", brand: "Fender", price: 950)
end
Each store gets their own instruments, customers, and orders â completely separate from the others.
A Bit About Models
I kept it pretty simple:
rails generate model Instrument name:string brand:string price:decimal
rails generate model Customer name:string email:string
rails generate model Order customer:references total:decimal
These models live in the tenant schemas. Global stuff (like platform admins) stays in the public schema â thatâs what the excluded_models setting is for.
Running Migrations
One thing to watch out for: every time you change the schema, youâll want to run the migrations across all tenants. The gem gives you a rake task for that:
rake apartment:migrate
If you forget this, some tenants will have outdated table structures â been there, done that .
What Worked Well
- I liked how clean and isolated the data was per store.
- Switching schemas based on subdomain worked great.
- Onboarding new stores was simple â just Apartment::Tenant.create(âŚ) and youâre good to go.
A Few Gotchas
- Migrations can be tricky. Youâll need to be careful testing them in multiple tenants.
- Avoid jumping between tenants too much inside the same thread or request â always make sure you reset.
- Itâs not the best fit if youâre managing thousands of tenants â PostgreSQL has limits on schema counts, and you might hit performance issues eventually.
Final Thoughts
If youâre building a multi-tenant Rails app and want strong data isolation, apartment is worth considering â especially if youâre already using PostgreSQL.
It saved me a ton of work and let me keep one codebase while still giving each music store their own âspaceâ to operate in. Just be ready to get your hands a little dirty with migrations and schema management.
If youâre trying it out and hit a wall, feel free to reach out. I probably ran into the same thing at some point.
Top comments (0)