DEV Community

Cover image for Multi-Tenancy in Laravel: Database-per-Tenant vs. Shared Database — A Practical Guide
WISSEN BERATUNG
WISSEN BERATUNG

Posted on

Multi-Tenancy in Laravel: Database-per-Tenant vs. Shared Database — A Practical Guide

If you're building a multi-tenant SaaS with Laravel, you'll face this decision early: shared database (all tenants in one DB with a tenant_id column) or database-per-tenant (each tenant gets their own database).

After running a database-per-tenant architecture in production for WB-CRM, here's a practical comparison.

Shared Database (Single-DB)

// Every query needs tenant scoping
Lead::where('tenant_id', $currentTenantId)->get();

// Risk: forget the scope → data leak
Lead::all(); // Returns ALL tenants' data!
Enter fullscreen mode Exit fullscreen mode

Pros: Simple migrations, easy cross-tenant queries, lower operational complexity.
Cons: One bug = cross-tenant data exposure. GDPR Art. 17 deletion is complex (cascade through all tables). Hard to offer dedicated instances.

Database-per-Tenant

// stancl/tenancy switches the default DB connection
// No tenant_id needed — the database IS the scope
Lead::all(); // Returns only current tenant's data
Enter fullscreen mode Exit fullscreen mode

Pros: Architecturally impossible to leak data. Trivial backup/restore/deletion per tenant. Can offer dedicated instances.
Cons: Migrations run N times. Cross-tenant queries require connection switching. More complex connection pooling.

The Practical Reality

Migrations: We have ~80 tenant migrations. With 50 tenants, that's 4,000 migration operations per deploy. We parallelize via queue workers:

php artisan tenants:migrate --parallel=4
Enter fullscreen mode Exit fullscreen mode

Central vs. Tenant models:

// Central model — MUST set connection explicitly
class Plan extends Model {
    protected $connection = 'central';
}

// Tenant model — NO $connection property (rely on bootstrapper)
class Lead extends Model {
    // stancl/tenancy handles connection switching
}
Enter fullscreen mode Exit fullscreen mode

The #1 gotcha: If you put $connection = 'mysql' on a central model instead of $connection = 'central', it will silently query the tenant database when tenancy is initialized.

When to Choose What

Factor Shared DB DB-per-Tenant
< 100 tenants Either works Recommended for B2B
GDPR compliance critical Possible but harder Recommended
Cross-tenant analytics Easy Requires extra work
Dedicated instances Not possible Natural extension
Cost Lower Slightly higher

For B2B SaaS in regulated industries (healthcare, finance, legal), database-per-tenant is worth the operational overhead. For B2C with millions of users, shared DB makes more sense.


Built with these patterns: WB-CRM — free CRM for SMBs, hosted in Germany.

Top comments (0)