← All writing

Multi-tenant SaaS, explained with a football academy

· 7 min read · by Rahul Dhileep Kumar

Multi-tenancy, explained with academies sharing one building and roles that open different rooms. Silo, pool and bridge models, noisy neighbours, data leaks and role-based access, for beginners.

Three footballs lined up on artificial grass while a squad in black kit warms up on the pitch behind them

"Multi-tenant" is one of those words that sounds harder than it is. I put it on my CV. Track My Academy is multi-tenant and role-based. Real academies run on it.

So here's what that actually means, for beginners. No cloud diagrams. Just a football academy, a building, and some locked doors.

One note before we start. This is about the general idea and the textbook patterns. It is not a tour of how Track My Academy is wired inside.

Three footballs lined up on artificial grass while a squad in black kit warms up on the pitch behind them

Photo: Leo_Visions on Unsplash

The building: what a tenant is

Picture one big sports building. Lots of academies rent space in it. Each one has its own players, coaches, fees and attendance.

Every academy is a tenant. The building is your software.

Microsoft's guide to tenancy models says that if your customers are other organisations, you usually map each tenant to one customer. It also says a single tenant typically has many users. One academy, many people.

That's the whole trick of SaaS. You build the building once. Everyone rents.

The rooms: roles

Inside one academy, not everyone gets the same keys.

That is role-based access control, or RBAC. NIST, who formalised the model back in 1992, says it has become the predominant model for advanced access control because it reduces the cost of managing access. Users get roles. Roles get permissions. You don't hand out keys one person at a time. You hand out "coach keys".

The model was later adopted as a standard, ANSI/INCITS 359, first in 2004. Not a new idea. Just a very useful one.

Here's the bit people mix up. Roles decide which rooms you can enter inside your academy. Tenancy decides which academy's rooms you can see at all. A coach at Academy A should never see Academy B's attendance, even with full coach keys. Microsoft's guide puts it as needing to consider both tenant and user identity when you authorise anything.

Three ways to share a building

AWS's SaaS Lens names three patterns: silo, pool and bridge.

Silo. Every academy gets its own building. In software terms, dedicated resources per tenant: its own stack, or at least its own database. AWS notes that even a silo still shares one identity, onboarding and operations setup. Otherwise you're running a managed service, not SaaS.

Pool. Everyone shares one building. Same servers, same database, same tables. AWS calls it "the more classic notion of multi-tenancy".

Bridge. A mix. Some parts siloed, some pooled. AWS says this acknowledges that "SaaS businesses aren't always exclusively silo or pool."

Microsoft describes the same spectrum, from fully isolated to fully shared, and says to treat isolation as a spectrum, not a switch.

Training lanes marked with white lines on a black surface, with white hurdles and yellow cones in separate lanes

Photo: Steve Pancrate on Unsplash

Same surface, separate lanes. That's roughly the picture. In the database, it usually looks like one of these:

ModelAcademy analogyTrade-off
Database per tenantEach academy has its own buildingStrong isolation, costs grow with every new tenant
Schema or collection per tenantOwn floor, shared buildingIn between, and more things to manage
Shared tables with a tenant_idShared rooms, every item labelled with the academy's nameCheapest, but every query must filter correctly

MongoDB's own multi-tenant guidance is blunt about the middle option. It says to "avoid putting all tenants with their own collections into a single database." It suggests a database per tenant for a small, stable number of tenants with strict access needs. And shared collections when tenant numbers keep growing.

With shared collections, MongoDB says: "Every document must have a tenantId field, and every database query must filter by that field." That sentence is the whole risk of the pool model.

The noisy neighbour

Shared buildings have one classic problem. One academy throws a party and the whole building shakes.

Microsoft calls it the noisy neighbour antipattern. It happens "when one tenant's performance is degraded because of the activities of another tenant." Say one academy runs a giant report while another is marking attendance at the gate. The second one waits.

The fixes on that page are sensible. Monitor usage per tenant, not just overall. Apply quotas, throttling or rate limits. Run heavy, non-urgent jobs at off-peak times. Cap expensive queries.

The silo model mostly avoids this. Microsoft's tenancy guide says dedicated deployments make tenants unlikely to affect each other's performance. But if one tenant costs X, it says, 100 tenants probably cost 100 times that.

The scary one: data leaks

Noisy neighbours are annoying. Leaks are a different category.

AWS's whitepaper on tenant isolation strategies says crossing that boundary "would represent a significant and potentially un-recoverable event for a SaaS business." Imagine a parent opening the app and seeing another academy's fee list. That's not a bug report. That's a trust problem.

In a shared database, one forgotten filter is enough. So I think the safest habit is to not rely on remembering. Make the database refuse.

PostgreSQL has row-level security for exactly this. Once it's on and no policy exists, it uses default-deny: no rows are visible or can be modified. Supabase's RLS guide builds on the same feature and points out it still protects data when it's reached through third-party tools.

Here's a small illustrative example. It is not Track My Academy's code, just the pattern:

-- Example only: a shared attendance table, labelled by academy.
ALTER TABLE attendance ENABLE ROW LEVEL SECURITY;

CREATE POLICY academy_isolation ON attendance
  USING (academy_id = current_setting('app.current_academy')::uuid);

The app sets app.current_academy for each request. Postgres then filters every query on that table to that academy, even one where someone forgot the WHERE.

Two catches from the Postgres docs. Superusers and roles with BYPASSRLS always skip these policies. Table owners do too, unless you use FORCE ROW LEVEL SECURITY. So the app shouldn't connect as the owner.

How I'd think about it

My opinion, for a small team:

  1. Decide what a tenant is on day one. For academy software it's obvious. For other products it often isn't.
  2. Put the tenant ID on everything. Retrofitting it later is miserable.
  3. Let the database enforce isolation where it can. Code reviews miss things. Default-deny doesn't get tired.
  4. Keep roles and tenancy separate in your head. Different questions, different checks.
  5. Watch per-tenant usage. You can't fix a noisy neighbour you can't see.
  6. Test for leaks on purpose. Microsoft's guide says to verify one tenant's data isn't leaked to another, whatever model you pick.

For context, the very first Track My Academy prototype had no database at all. The Excel sheet was the database. A long way from tenancy models.

FAQ

What is a tenant in SaaS?

A customer, usually an organisation, whose data and users are kept separate from every other customer's, even though they all use the same software.

Is a shared database with a tenant ID safe?

It can be, if every query is filtered by tenant and you add a database-level guard like row-level security. The risk is a single missed filter.

What's the difference between multi-tenancy and RBAC?

Tenancy keeps organisations apart. RBAC decides what each person can do inside their own organisation.

Sources

What next

How it started, Excel sheet and all: a coach complained, so I started a company. And what happens when the shared building goes dark: the night I broke production.

Running an academy? Track My Academy handles attendance, fees, player reports and parent messages, and there's a free plan.