Multi-tenancy requires Spree Enterprise Edition license.
- customer accounts
- staff accounts (admin users), staff can manage multiple tenants (it’s the standard invitation flow)
- products, categories, and other catalog data
- orders and order history
- shipping and payment methods
- tax rates and zones
- store settings (name, logo, etc.)
- etc.
If you need individual seller/supplier/vendor accounts but shared product listings under one site you should use multi vendor recipe instead.
Prerequisites
- You need to be on Spree 5.1+, we recommend using CLI to setup your Spree application.
- You need to set 2 environment variables in your
backenddirectory:KEYGEN_ACCOUNT_IDKEYGEN_LICENSE_KEY
- Both PostgreSQL and MySQL are supported
You will need to add these environment variables to your CI/CD pipeline and staging/production environments.
Installation
Eject the Spree backend
If you’ve created your project withcreate-spree-app you will need to eject the backend to be able to install the Enterprise and Multi-Tenant gems. You can do this by running:
Adding gems
-
Add the following code to your
backend/Gemfile: -
Configure the Docker image build to authenticate against the Keygen source.
If you use the Spree CLI (Docker),
spree bundle installresolves the licensed gems inside the image at build time, so the Keygen credentials must be available duringbundle installin theDockerfile— not just at runtime. Pass them as BuildKit secrets so they are mounted as environment variables for thebundle installstep only, and never baked into the image layers or history (unlikeARG/ENV/COPY).Wrap everySkip this step if you install without the Spree CLI (plainbundle installon the host). In that case the Gemfile readsKEYGEN_LICENSE_KEY/KEYGEN_ACCOUNT_IDdirectly from your shell environment.bundle installin yourbackend/Dockerfilewith the secret mounts. There are usually two — one in the build stage and one in the dev stage:Then wire the secrets into both compose files (backend/Dockerfiledocker-compose.ymlanddocker-compose.dev.yml— the latter is whatspree ejectcopies over) so the CLI supplies them to the build:docker-compose.ymlCompose readsenvironment:-sourced secrets from the shell environment that runs the build, not from.env. Make sureKEYGEN_LICENSE_KEYandKEYGEN_ACCOUNT_IDare exported in the shell (and in your CI/CD and production build environments). -
Install gems:
-
Run generators:
This will copy and run migrations for
spree_enterpriseandspree_multi_tenantgems. Thespree_multi_tenant:installgenerator also editsconfig/routes.rb(to add the tenant domain routing constraints), your customer user model, and your admin user model — see Post-install configuration below.
Post-install configuration
Setting root domain
Usually multi-tenant applications are configured to use subdomains for each tenant. For example, if your root domain isexample.com, you can have tenants like tenant1.example.com, tenant2.example.com, etc.
To make it work you need to set the Spree.root_domain in your config/initializers/spree.rb file, eg.
lvh.me for local development, so cross-subdomain cookies will work, localhost will not work.
Sharing the session cookie across subdomains
For staff to stay signed in while moving between the app domain (app.example.com) and tenant
subdomains (store1.example.com), the session cookie must be scoped to the parent domain. Set
COOKIE_TLD_LENGTH to the number of labels in your root domain:
.env
Allowing tenant subdomains through host authorization
Rails’ host authorization blocks unknown hosts..localhost and .test are permitted in
development by default, but .lvh.me (and your production domain) are not — requests to
app.lvh.me are rejected with a “Blocked hosts” error until you allow them. Add your root
domain to config/environments/development.rb:
backend/config/environments/development.rb
Customer User Class adjustment
We need to make slight adjustments to the user class so it can work in a multi-tenant environment. Thespree_multi_tenant:install generator adds include SpreeMultiTenant::CustomerUserConcern
to your customer user model automatically. You then need to remove the :validatable module
manually. :validatable validates the user’s email uniqueness globally; the concern re-validates
it in the scope of the tenant instead, so the same email can exist across different tenants.
backend/app/models/spree/user.rb
Admin User Class adjustment
The generator also changes your admin user model’s base class fromSpree.base_class to
Spree::Base. If your admin_user.rb exists, this is applied automatically; otherwise make the
change yourself:
backend/app/models/spree/admin_user.rb
Unlike the customer user model, the admin user model keeps
:validatable — staff accounts are
global and can manage multiple tenants, so their emails remain globally unique.
