
For most B2B SaaS products, the right starting point for multi-tenancy is a single database with a tenant_id column and a global scope — not a database-per-tenant split. Database-per-tenant solves a problem most teams don't have yet and creates several they will: migration fan-out, cross-tenant reporting pain, and connection-pool exhaustion at scale.
Enforce tenancy at the model layer, not the controller layer
The most common multi-tenant bug is a controller or job that forgets to scope a query. Fix the failure mode at the source with a global scope so it's structurally impossible to forget.
class BelongsToTenant extends Model
{
protected static function booted(): void
{
static::addGlobalScope('tenant', function (Builder $query) {
if ($tenantId = app(CurrentTenant::class)->id()) {
$query->where('tenant_id', $tenantId);
}
});
static::creating(function (self $model) {
$model->tenant_id ??= app(CurrentTenant::class)->id();
});
}
}Index for tenancy from day one
Every tenant-scoped table needs a composite index that leads with tenant_id — otherwise queries that look fast in development degrade badly once a handful of large tenants share the table. Pair that with row-level constraints (unique indexes scoped to tenant_id) so uniqueness rules can't leak across tenants.
- Add tenant_id to every tenant-owned table, indexed as the leading column
- Enforce scoping with a global scope, not per-query discipline
- Scope uniqueness constraints to tenant_id explicitly
- Move to schema- or database-per-tenant only when a customer requires physical isolation for compliance
Should I use separate databases for each tenant in Laravel?
Only when a customer contractually requires physical data isolation. Otherwise, row-level tenancy with a global scope is simpler to operate, cheaper to run, and easier to report across.
Hayden Jackson
Get engineering notes in your inbox
Practical AI, cloud, and security delivery notes - no fluff, no sales spam.

