Last month, I delivered a Laravel application that serves 12 different businesses across the UAE, all from a single codebase. The client was a franchise network in Dubai — their old system required separate dashboards for each branch, which made updates a nightmare. They wanted everything unified. I proposed multi-tenancy. Here’s what actually worked.
The Multi-Tenancy Decision
Let’s be real: multi-tenancy isn’t always the right call. If your clients have wildly different requirements, you’ll dig yourself into a configuration mess. But in this case, all 12 businesses operated under the same core structure — think retail franchises with similar inventory and staff management needs. The upside of sharing a codebase was obvious: easier updates, centralized monitoring, and cost-effective maintenance.
I spent two days researching packages. Laravel’s core doesn’t include multi-tenancy, so I benchmarked landlord vs Stancl’s Tenancy. The deciding factor? Stancl’s support for separate PostgreSQL databases per tenant aligned with UAE data privacy laws requiring customer data isolation.
Database Structure: Why Separate DBs Won?
We initially tried a shared database with tenant_id columns, then switched to separate databases. The tipping point came when testing performance at scale — a client’s test dataset with 200k+ records per tenant caused query times to spike by 40% in shared mode. Isolation gave us better speed guarantees (plus easier backups/destroyal).
Here’s the setup:
- •Tenant identification via subdomains (
business1.platform.ae,business2.platform.ae) - •PostgreSQL DB per tenant
- •Shared Redis cache for global lookup tables
- •UUIDs instead of auto-incrementing IDs to avoid collision risks
Migration Hell and How We Survived
First major snag: seeding data. Laravel’s factories didn’t respect dynamic database connections out-of-box. I spent 12 hours fighting with custom factory resolver classes before finding a workaround using DB::setTablePrefix() in the tenant bootstrapping layer.
Even worse: forgetting to include the tenant ID when setting up a foreign key between inventory items and suppliers. That caused a cascade of errors when business A’s staff accessed business B’s supply list, and cost me two days of debugging.
UAE Localization: Arabic + Hijri Dates
Twelve tenants meant twelve config files for localization. But here’s the catch: four clients required Arabic interfaces with Hijri calendars. I added a tenant-config/{tenant_id}/lang/ directory structure, and used Laravel’s setlocale() to dynamically load date formatting based on domain.
This wasn’t in the original specs, and honestly, I should’ve asked more questions during discovery. That said, the final system handles:
- •Arabic/Dhivehi/English mix for text
- •Dual calendar system (Gregorian + Hijri)
- •Right-to-left layouts via a per-tenant
rtlboolean flag in config
Real-World Test Case: Tawasul Limo
This reminded me of a similar project: Tawasul Limo, a Laravel+Next.js limo booking platform. They didn’t use multi-tenancy, but we applied similar database isolation principles for VIP clients with compliance requirements (GDPR, UAE Data Law). It’s running on 23 servers across Middle East data centers now.
Main difference? Tawasul had 3 major variants; this platform serves 12 tenants with 95% code reuse.
Deployment Gotchas
Docker became my best friend. We built a base image for the core app, then a script that generated tenant-specific Dockerfiles with config overrides. Pushing updates meant rebuilding only the core layer, then restarting containers — total downtime: 2-4 seconds per tenant.
But here’s a weird local quirk: some government agencies in the UAE flag generic SSL certificates as “untrusted” on internal networks. We ended up buying Comodo certs branded under the client’s company name for 7 tenants.
What I’d Do Differently
Versioned deployments for each tenant would’ve saved time. When the first two clients hit production bugs, we had to roll back for everyone. Later we added tenant-aware version tags (v3-businessX-12-08-2025) using Laravel Envoy, which let us isolate fixes.
Also, don’t skimp on logging. We used Laravel Telescope per-tenant database connections to track which business triggered which job — worth it when debugging cross-tenant queue issues.
Building this system taught me that multi-tenancy isn’t just a technical challenge — it exposes messy client expectations. One Abu Dhabi tenant insisted their UI must use a specific gradient in the logo, which took 48 hours of back-and-forth about “brand consistency.”
If you’re staring at a multi-tenancy problem, hit me up on sarahprofile.com/contact — I’ll share the tenant package comparison spreadsheet I never published.