When LekkerGeven Groningen asked me if they "could also use the site for Utrecht," I faced a choice. Option A: copy the entire codebase, rename variables, deploy to a new folder. Option B: build it so that a new city is a config file.

We chose B. And it was one of the best decisions of the entire project.

What is multi-tenancy?

One application serving multiple clients or brands — each with their own look, data and settings — but all running on the same system behind the scenes. A "tenant" can be a city, a franchise, or a customer of your platform. They never see each other's data.

How we built it

1. Tenant identification

For each request we determine which tenant is active based on the subdomain. groningen.lekkergeven.nl → tenant_id 1. utrecht.lekkergeven.nl → tenant_id 2. That ID follows through the entire request.

2. Data scoping

Every database query automatically gets a WHERE tenant_id = X. No escape possible. One bug and a Groningen user sees Utrecht donations — so this is non-negotiable.

3. Theme + content per tenant

Per tenant a config file with:

  • Color palette (CSS custom properties)
  • Logo + favicon
  • City-specific texts (about us, contact)
  • Mollie API keys (so money goes to the right account)
Rolling out a new tenant takes 30 minutes. Launching a new city is a config file, not a project.

4. Shared admin, separate administrators

The admin panel has one login system, but user roles are tenant-specific. A Groningen admin only sees Groningen data. The super-admin (me) sees everything, for support.

What we did wrong

First we tried one database per tenant. Nice and clean — but when rolling a schema update I had to migrate each database separately. With 4 cities that's manageable. With 40 it's not.

We switched to one database, tenant_id everywhere. Migrations are now one command. The extra discipline is worth it.

When should you not do this?

If you'll never have more than one client. Or if tenants need completely different features (then you're essentially building different products). Multi-tenancy only makes sense when most functionality is the same and only branding/data differs.

For LekkerGeven that was perfect. For your idea maybe too — let's take a look sometime.