
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.
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.
