← Vybix Blog

Multi-tenant architecture:
what to get right on day one.

Multi-tenant architecture is not a feature you bolt on later. It is the foundation. Get it wrong on day one and you are looking at a 6-month rewrite when the second customer signs up.

A multi-tenant SaaS is one application serving many isolated customers (tenants). One codebase. One database. Many companies whose data must never accidentally bleed into each other. The architectural decisions you make in week one determine how cleanly the system can grow from 1 tenant to 10,000.

The three approaches — pick one, and pick on purpose

  • Schema-per-tenant. Every tenant gets its own database schema. Strong isolation. Easy backups per tenant. Painful for cross-tenant analytics. Best for enterprise SaaS with strict data residency or compliance needs.
  • Database-per-tenant. Even stronger isolation than schema-per-tenant. Each tenant gets a separate database. Excellent for regulated industries. Operations cost scales linearly with tenant count — fine for 50 tenants, painful at 5,000.
  • Shared schema with row-level isolation. All tenants share the same tables; every row has a tenant_id; Postgres row-level security (RLS) enforces the filter. Cheapest to operate, most flexible to query, scales to thousands of tenants. The right default for most SaaS.

We pick shared-schema-with-RLS for ~80% of new SaaS builds. Schema-per-tenant for enterprise with hard isolation requirements. Database-per-tenant rarely — usually for fintech or healthtech where regulators expect it.

Why row-level security is the multi-tenant superpower

Without RLS, every query in your application must filter by tenant_id. Forget one filter, leak another tenant's data. It is a tax on every line of code and a risk on every code review.

With Postgres RLS, the database itself enforces the filter. Even if your application code forgets the WHERE clause, the query will return zero rows for the wrong tenant. It is defence in depth, free, built into Postgres, and the single most important technical decision you can make on day one of a multi-tenant build.

How RLS actually works

On every connection, you set a session variable: SET LOCAL app.current_tenant = '...'. Every table that holds tenant data has an RLS policy: USING (tenant_id = current_setting('app.current_tenant')::uuid). The database transparently filters every query by that variable. Your application code reads and writes as if it were a single-tenant system; the database makes sure the right rows come back.

What to get right on day one

  • tenant_id on every tenant-scoped table. Not nullable. Foreign key to a tenants table. Indexed.
  • RLS policies enabled and tested. Write integration tests that try to access data from the wrong tenant and assert that zero rows come back.
  • The session-variable wiring. Either set app.current_tenant in a middleware that runs on every request, or use a connection pool that scopes per request.
  • A clear admin path that bypasses RLS. Your own staff (and your superadmin tools) need a separate role that can see across tenants. Mark it explicitly. Audit it.
  • Tenant onboarding and offboarding flows. Spinning up a new tenant, archiving an old one. Plan it now — not when the first customer asks to leave.

Billing quotas — the second silent killer

Multi-tenant SaaS that bills customers has a second hard problem: enforcing what each tenant is allowed to do based on their plan. Users on the Pro plan get 100,000 API calls a month; users on the Free plan get 5,000. Whose job is it to enforce that?

Three components you need on day one:

  1. A plan definition table that maps each plan to its limits.
  2. A usage counter per tenant, ideally in Redis for speed, materialised back to Postgres for billing.
  3. A middleware that rejects or throttles requests when the limit is hit.

Build this before you launch paid plans. Adding it later means refactoring every API endpoint and every billing webhook.

The mistakes we have watched (and rescued)

  • No tenant_id on early tables. A founder built a single-tenant MVP, raised seed, signed a second customer. The data leak risk was so severe the team had to spend two months retrofitting RLS before they could onboard.
  • tenant_id but no RLS. Application-level filtering only. One forgotten WHERE clause in an admin endpoint leaked customer data across tenants. Postgres RLS would have prevented it.
  • Database-per-tenant with no automation. Manually creating a new database per tenant worked for 5 customers. By 50 customers, the team was spending half its time on database operations.
Multi-tenancy is not a feature you add when the second customer signs. It is an architecture you build on day one. Retrofitting it costs more than building it correctly from scratch.

What we ship for every multi-tenant SaaS

On every multi-tenant SaaS we build at Vybix:

  • Shared-schema architecture with tenant_id on every tenant-scoped table.
  • Postgres row-level security policies on every table, tested with integration tests that assert isolation.
  • Tenant onboarding flow — a single API call that creates the tenant, seeds default data, and sends the invite email.
  • A plan / usage / quota system wired in from week 8.
  • Stripe (or Razorpay) subscription billing with idempotent webhook handling, dunning, and plan upgrade/downgrade logic.
  • A superadmin tool that lets us debug a specific tenant's data with explicit, audited access.

The bottom line

If you are building anything multi-tenant — and most B2B SaaS is multi-tenant — get the foundation right on day one. Postgres RLS, tenant_id discipline, billing quotas, and tenant lifecycle flows. The week of extra thought up front prevents the quarter of expensive retrofitting later. See our SaaS development service for how we structure this on real projects.

Frequently asked questions

What is the simplest multi-tenant pattern?

Shared schema with row-level isolation enforced by Postgres RLS. Every tenant-scoped table has a tenant_id column with an RLS policy. Easiest to operate, scales to thousands of tenants, hardest to leak data out of.

When should I use schema-per-tenant or database-per-tenant?

Schema-per-tenant: when you need strong isolation and have under ~500 tenants. Database-per-tenant: when regulators require it (some fintech, healthtech). For everything else, shared-schema with RLS is the right answer.

Can I retrofit RLS into an existing system?

Yes, but it is painful. You will spend weeks adding tenant_id to existing tables, writing migrations, and updating queries. Far cheaper to build RLS in from day one.

Does RLS slow down queries?

Marginally — usually unmeasurable. The optimiser pushes the RLS predicate into the query plan as if you had written WHERE tenant_id = '...' yourself. Indexes on tenant_id remove any meaningful overhead.

Do you do compliance work (SOC 2, GDPR)?

We architect for it. Audit logs, encryption, role-based access, and data residency are part of every multi-tenant build. Full certification audits are done by specialist firms — we hand them an audit-ready system.

Want this kind of thinking applied to your product?

We're an engineering studio in Kerala building production software for startups across India and worldwide. If you're scoping a real project — MVP, SaaS, web app, or mobile — we'd love to talk.

Book a discovery call →