Architecture

Database per tenant or shared schema for healthcare SaaS: how we decide

Isolation, backups, migrations and cost, compared the way we compare them with a client before a single table is created.

Every healthcare SaaS project we start reaches the same question in the first week: do all customers share one database, or does each get its own? The honest answer is that both work. The right one depends on who your customers are and what a data leak costs them.

What the shared schema gives you

In a shared schema design, every table has a tenant_id column and the application adds a filter to every query. It is simple to build, cheap to run, and it is how most Laravel multi-tenancy tutorials start. For a product with thousands of tiny customers, such as a booking tool for solo practitioners, it is often the correct choice.

The weakness is that isolation lives in application code. One missed scope on a report query, one raw SQL join, one background job that forgets to set the tenant, and the wrong clinic sees a patient. Global scopes help, and we use them, but they are a habit and not a wall.

What database per tenant gives you

With one database per tenant, the connection itself is the boundary. The request resolves the tenant from a verified token, the app switches the connection, and from then on a query cannot name another tenant's tables because they are not reachable.

  • A missed WHERE clause cannot leak across tenants, because there is nothing to leak across.
  • Backups, restores, exports and deletions happen per tenant, which is how clinics and franchise owners ask for them.
  • Encryption keys can differ per tenant, so one compromised key does not open everything.
  • You can move a large tenant to its own server without redesigning the product.
  • A tenant leaving means dropping a database and a key, which is a clean, provable deletion.

What it costs you

More databases means more operations. Migrations must run against every tenant, in order, and fail safely. Monitoring grows. Cross tenant reporting needs a plan. None of this is hard, but it has to be built before the tenth customer, not after the hundredth.

// Per request: resolve tenant, then point the default connection at it
$tenant = Tenant::fromVerifiedToken($request->bearerToken());
config(['database.connections.tenant.database' => $tenant->database]);
DB::purge('tenant');
DB::setDefaultConnection('tenant');
Never accept a tenant identifier from the client body or a header the user controls. The tenant comes from the signed token, and only from there.

How we decide

  • Multi-location groups, franchises and enterprise customers who will ask about data separation: database per tenant.
  • Thousands of small customers with similar data and low risk per customer: shared schema with strict scopes and row level safeguards.
  • A mix: shared schema for the long tail and a dedicated database for large or regulated customers, decided by plan.

Operational habits that make it work

We keep a small control database that stores tenants, plans and feature flags and never any patient data. Migrations are versioned and run per tenant by a job that records success and failure. New tenants are created by an automated onboarding job, so adding a clinic is a button, not a project. Health checks alert on a tenant that falls behind on migrations.

Cross tenant reporting is the usual objection. We solve it with scheduled, de-identified aggregates written to a reporting store, or with per tenant queries that a service combines. Both avoid copying raw patient data into one big shared table.

Signedrequest Applicationtenant routertoken to connection Tenant A databaseown key, own backups Tenant B databaseown key, own backups Tenant C databaseown key, own backups Control planetenants and plans only
Each request resolves to one tenant database. The control plane holds settings only.

Planning something like this?

We would be glad to look at your architecture or your plan. We sign an NDA and a BAA before you share anything sensitive.

Healthcare software development
Questions we hear

Related questions

Is database per tenant required for HIPAA?

No. HIPAA asks for reasonable and appropriate safeguards and does not name an architecture. Database per tenant is a strong way to meet access control and integrity goals, and many buyers prefer it, but a well built shared schema can also be defensible.

Does it make Laravel apps slower?

Not in normal use. Switching a connection per request is cheap. The real cost is operational: migrations, monitoring and backups across many databases, which we automate.

Planning a healthcare platform? Start with a conversation, not a quote.

A senior engineer will review what you have, ask about your data flows and tell you honestly whether we are the right team. We sign an NDA and a BAA before you share anything sensitive.

Request our BAA