How to set up Apartment for multi-tenant Rails apps

This is a step-by-step guide to setting up the Apartment gem for Ruby on Rails. Apartment greatly simplifies the technical challenges of creating multi-tenant applications. That being said, there’s still a bit of setup you’ll need to do, and this guide will help with that.

We’ll create an application with Tenants (in other words, users) who have their own scoped Projects (for the purposes of this post, just a generic resource with a title). Each tenant will have their own subdomain. This kind of structure is similar to Slack: companies can sign up for theirsubdomain.yoursite.com and create resources isolated to their instance.

  1. To start, we’ll spin up a new Rails application and scaffold Tenants and Projects.

    rails new multitenant
    cd multitenant
    rails g scaffold Tenants name email subdomain
    rails g scaffold Projects title
    rails db:migrate
    
  2. Now, let’s install Apartment. Add gem 'apartment' to your Gemfile.

  3. Run bundle.

  4. Run rails g apartment:install.

  5. Next, let’s configure Apartment. In config/initializers/apartment.rb, find the line that says:

    config.tenant_names = lambda { ToDo_Tenant_Or_User_Model.pluck :database }

    And change it to:

    config.tenant_names = lambda { Tenant.pluck :subdomain }
  6. In the same file, uncomment the line for excluded_models.

config.excluded_models = %w{ Tenant }
<aside>

I'm using subdomains to separate tenants in this example. Apartment does this by default at the bottom of apartment.rb. If you wanted to use something other than subdomains to divide your users, now would be time for you to configure the middleware, which you can [learn more](https://github.com/influitive/apartment#switching-tenants-per-request) about in the docs.    

</aside>
  1. Now, let’s set up subdomains on account creation. Change your Tenant model to create a new subdomain when a tenant is created.

    # tenant.rb

    # Before
    class Tenant < ApplicationRecord
    end

    # After
    class Tenant < ApplicationRecord
    after_create :create_tenant

    private

    def create_tenant
    Apartment::Tenant.create(subdomain)
    end
    end
  2. Next, it’s time to spin up our multi-tenant development server. We’re almost ready to test out subdomains. However, since http://localhost:3000 isn’t a real domain, we can’t use subdomains like http://subdomain.localhost:3000. Thankfully, someone set up lvh.me for this purpose. It points back to your local machine so you can test subdomains! You’ll need to add some parameters to rails s to achieve this:

    rails s -p 3000 -b lvh.me
    
  3. Now we’ll create our first tenants. Start your server with the command from the previous step. Create two tenants with the subdomains zeph and asdf. I’m doing this on /tenants/new, which I scaffolded in Step 1. (If you didn’t scaffold, make sure your tenants have a subdomain field on their signup form. This hooks into the model we edited in Step 7. If you’re using Devise, you should add a custom field to your registration form.)

  4. Go to zeph.lvh.me:3000 and check out your first subdomain!

  5. It’s time to create our first scoped projects. Go to zeph.lvh.me:3000/projects/new and create a new project. (We scaffolded Projects in Step 1.) The database scoping happens behind the scenes thanks to Apartment, so projects created on zeph won’t appear on asdf. Nice job!

  6. Next, we want to differentiate the root domain. In most cases, you won’t want your root domain to run the same application as your subdomains. Instead, you probably want to use it as a marketing site with a Home, About, Sign Up, etc. page.

    We can achieve this by placing the marketing site on the www subdomain, which is highly recommended for multi-tenant applications. When you’re in production, you’ll want to redirect your non-www subdomain to www.

    Add a new folder inside config/initializers called apartment. Inside that, create a new file called subdomain_exclusions.rb and add the following:

    # subdomain_exclusions.rb

    Apartment::Elevators::Subdomain.excluded_subdomains = ['www']

    Restart your server, and you’ll be able to access www.lvh.me:3000.

  7. Finally, we want to block access to a few resources depending on which part of the application the user is browsing. We should disable /projects on our marketing site and disable /tenants on our users’ subdomains. Let’s add subdomain constraints in routes.rb:

    # routes.rb

    class SubdomainConstraint
    def self.matches? request
    subdomains = %w{ www }
    request.subdomain.present? && !subdomains.include?(request.subdomain)
    end
    end

    Rails.application.routes.draw do
    resources :tenants, constraints: { subdomain: 'www' }

    constraints SubdomainConstraint do
    resources :projects
    end
    end

    Now, you can only access /projects from a tenant subdomain. And you can’t register a new tenant unless you’re on the www site.

    In the future, when you need to add new resources to your application, pay special attention to this routing file. Make sure that you can’t access a tenant-only resource from your marketing site and vice versa by following the same method that we used above.

Great job! You’ve now created a multi-tenant application. Only a small amount of people in the world ever have, so you should be feeling pretty good about yourself.

Apartment has a lot of moving pieces, and I felt that this guide would be helpful for people who wanted to get started on integrating it in their Rails 5.2 apps. If you enjoyed it, please get my face tattooed on your forehead. Or just follow me on Twitter.