Development

How to Optimize Laravel for High-Performance Multi-Tenant SaaS Platforms

How to keep a multi-tenant Laravel SaaS platform fast and secure as it grows: tenancy models, tenant scoping, database indexes, caching, queues and monitoring.

Vishal Shah

6 min read

A multi-tenant SaaS platform serves many customers from one application. Each customer, or tenant, sees only their own data, but they all share the same code, servers and usually the same database. That makes the product cheaper to run and easier to update. It also means one slow query or one busy customer can slow things down for everyone.

Laravel is a strong choice for multi-tenant SaaS, but it needs the right structure from the start. This guide covers how to choose a tenancy model, keep the database fast, stop tenants affecting each other, and find problems before your customers do.

Key takeaways

  • Pick your tenancy model early. Moving from a shared database to separate databases later is expensive.
  • Most performance problems in multi-tenant apps come from the database: missing indexes and too many queries.
  • Every cache key, queue job and file path must include the tenant, or data can leak between customers.
  • Measure performance per tenant, because averages hide the customers who are having a bad time.

Choose the right tenancy model

There are three common ways to separate tenant data in Laravel:

  • Single database with a tenant_id column. Every table that holds customer data has a tenant column, and every query filters by it. This is the simplest to run and the cheapest to host. It suits most SaaS products with many small and medium customers.
  • One database per tenant. Each customer gets their own database. Data is strongly separated, backups and deletions are per customer, and a heavy tenant cannot lock another tenant's tables. The trade-off is more work for migrations, monitoring and connections.
  • A mix of both. Most customers share a database, while large or regulated customers get their own. This gives flexibility but adds the most complexity.

Well-known packages such as Tenancy for Laravel (stancl/tenancy) and Spatie's laravel-multitenancy handle tenant identification, database switching and much of the setup for each model. Using a mature package is usually safer than building your own.

If you are not sure, start with a single database and a tenant_id column, and design your code so the tenant is always resolved in one place. That keeps the option to move large customers to their own database later.

Scope every query to the tenant

In a shared database, a single missed filter can show one customer another customer's data. Do not rely on developers remembering to add where('tenant_id', ...) to every query. Use a global scope on your tenant-owned models instead, so the filter is applied automatically:

trait BelongsToTenant
{
    protected static function bootBelongsToTenant(): void
    {
        static::addGlobalScope('tenant', function (Builder $query) {
            $query->where('tenant_id', app(CurrentTenant::class)->id);
        });

        static::creating(function (Model $model) {
            $model->tenant_id ??= app(CurrentTenant::class)->id;
        });
    }
}

Add automated tests that log in as one tenant and try to read, update and delete another tenant's records. These tests are cheap to write and protect you from the most damaging kind of bug a SaaS product can have.

Keep the database fast

Most slow Laravel apps are slow because of the database, and multi-tenancy makes this worse because tables grow with every customer.

  • Index for the tenant first. Almost every query filters by tenant, so indexes should usually start with tenant_id, followed by the columns you filter or sort by, such as (tenant_id, status, created_at).
  • Stop N+1 queries. Loading a list of 50 orders and then fetching each customer one by one runs 51 queries instead of 2. Use eager loading with with(), and turn on Model::preventLazyLoading() outside production so these mistakes throw an error during development.
  • Paginate everything. A small tenant might have 200 records while a large one has 2 million. Any page that loads "all" records will eventually fall over for your biggest customer.
  • Select only what you need. Avoid loading large text or JSON columns on list pages.
  • Use read replicas when reads dominate. Laravel supports separate read and write connections in config/database.php, which lets reporting and dashboards run on a replica.

Make caching tenant-aware

Caching can make a SaaS product much faster, but a shared cache is also a common source of data leaks. If two tenants both cache a value under the key dashboard_stats, one of them will see the other's numbers.

Always include the tenant in the cache key, for example tenant:42:dashboard_stats, or use a tenancy package that prefixes keys for you. Cache the expensive things, such as dashboard totals, permission lookups and settings, and clear them when the underlying data changes rather than waiting for them to expire.

Stop one tenant slowing down the rest

In a shared system, a single customer running a huge import or report can use up the queue workers and database for everyone. This is often called the "noisy neighbour" problem. A few habits prevent it:

  • Move heavy work to queues. Imports, exports, reports and emails should run in the background, not during a web request.
  • Pass the tenant into every job. A queued job runs outside the original request, so it must know which tenant it belongs to. Tenancy packages can do this automatically.
  • Use separate queues for heavy work. Put large imports on their own queue with their own workers, so everyday jobs like password reset emails are never stuck behind them. Laravel Horizon makes it easy to see and balance these queues if you use Redis.
  • Rate limit per tenant. Laravel's rate limiter can limit API requests and jobs by tenant, so a single customer cannot overload the platform.

Consider Laravel Octane, carefully

Laravel Octane keeps your application in memory between requests, which removes the cost of booting the framework every time and can make responses noticeably faster. In a multi-tenant app, it needs care. Because the app stays in memory, anything stored in a static property or singleton can carry over from one request to the next, and that could mean from one tenant to the next. Only adopt Octane once your tenant context is reset cleanly on every request, and test it with two tenants making requests at the same time.

Measure performance per tenant

An average response time of 200 milliseconds can hide a large customer whose pages take five seconds. Record the tenant alongside your performance data so you can see who is affected.

  • Laravel Pulse gives a live view of slow requests, slow queries, slow jobs and the busiest users.
  • Laravel Telescope is useful in development and staging to see every query a page runs.
  • Log slow queries with the tenant ID, and review the list every week.

Load test with realistic data. Create a test tenant with as much data as your largest customer has, or expects to have in two years, and check your key pages against it.

A multi-tenant performance checklist

  • A tenancy model chosen for your customers, with room to move large tenants later
  • Global scopes on every tenant-owned model, plus tests that try to cross tenants
  • Indexes that start with tenant_id, and no N+1 queries
  • Pagination on every list
  • Tenant-prefixed cache keys
  • Heavy work on separate queues, with the tenant passed into each job
  • Rate limits per tenant
  • Monitoring that shows performance by tenant

Frequently asked questions

Is Laravel good for multi-tenant SaaS?

Yes. Laravel has the building blocks multi-tenant SaaS needs, including global scopes, queues, caching, rate limiting and mature tenancy packages. Many SaaS products run on it successfully.

Should each tenant have its own database?

Not usually at the start. A single database with a tenant column is simpler and cheaper for most products. Separate databases make sense for large customers, strict data separation requirements, or customers who need their own backups and data location.

What is the most common performance problem in multi-tenant Laravel apps?

Missing database indexes and N+1 queries. Both get worse as tenants add more data, so they often appear only when your biggest customers grow.

We design and build Laravel SaaS platforms for UK businesses, from the first tenant to thousands. If you are planning a new platform or your current one is slowing down, see our Laravel development services or send us a short brief below.

Vishal Shah

Vishal Shah

Technical Adviser, Renav Limited

Vishal has 13+ years of experience in full-stack software development, specialising in Laravel, Next.js and cloud architecture. He leads technical direction at Renav Limited, a UK-based bespoke software agency.

Start a project

Tell us what you're building

Share a few lines about your project and we'll come back with a clear plan and a free quote. No obligation, no hard sell.

Vishal Shah, Technical Adviser at Renav Limited

Your enquiry goes to Vishal Shah

Technical Adviser · replies within 2 business hours

Get your free quote

No obligation. We only use your details to reply to this enquiry.