Zum Inhalt springen

Laravel Multi-Tenancy: Mandantenfähigkeit richtig bauen

9 Min. Lesezeit

von Marcel, Senior-Softwareentwickler

Irgendwann kommt in fast jeder erfolgreichen Laravel-Anwendung der Satz: „Können wir das auch für unseren zweiten Standort haben – aber mit eigenen Daten?“ Ab da bist du im Thema Mandantenfähigkeit, auf Englisch Multi-Tenancy. Die Kernentscheidung ist schnell benannt und schwer zurückzunehmen: eine gemeinsame Datenbank, in der jede Zeile eine tenant_id trägt, oder eine eigene Datenbank pro Mandant. Für die meisten Anwendungen ist die gemeinsame Datenbank richtig. Die eigene Datenbank pro Kunde ist die Antwort auf harte Anforderungen – und sie kostet dich Komfort an Stellen, an die vorher niemand denkt.

Das Wichtigste in Kürze

  • Kernantwort: Eine Datenbank mit tenant_id ist der Standardweg – einfacher zu betreiben, günstiger, schneller zu ändern.
  • Eine Datenbank pro Mandant lohnt sich bei vertraglich zugesicherter physischer Trennung, sehr großen Einzelkunden oder wenn du einen Kunden einzeln wiederherstellen können musst.
  • Der gefährliche Punkt: Global Scopes schützen nur Eloquent-Abfragen. Rohes SQL und DB::table() umgehen sie – dort passieren die Datenlecks.
  • Migrationen sind der Moment, in dem getrennte Datenbanken wehtun: Jede Änderung läuft über alle Mandanten, und der Lauf kann mittendrin abbrechen.
  • Nachrüsten geht, ist aber Umbau am fahrenden System: nach Aufwand zu 79 €/h, ein abgegrenztes Pilotprojekt ab 3.900 €.

Wann eine Laravel-Anwendung mandantenfähig werden muss – drei Auslöser

Nicht jede Anwendung mit mehreren Kunden braucht echte Mandantenfähigkeit. Drei Situationen erzwingen sie:

  1. Aus einem Kunden werden mehrere, die sich nicht sehen dürfen. Der klassische Weg vom Auftragsprojekt zum Produkt. Ab dem zweiten Kunden ist „wir installieren die App einfach nochmal“ zwar verlockend, aber du pflegst ab da zwei Installationen, zwei Deployments, zwei Datenbanken – und beim zehnten Kunden zehn davon.
  2. Ein Kunde bekommt eine interne Struktur. Standorte, Filialen, Tochtergesellschaften, Franchise-Nehmer. Sie teilen sich die Stammdaten, aber nicht die Belege.
  3. Du baust ein SaaS-Produkt. Dann ist Mandantenfähigkeit keine Erweiterung, sondern das Fundament. Wer sie im MVP wegdenkt, zahlt sie später doppelt – wie ein Produkt von der Idee bis zu den ersten Kunden entsteht, haben wir separat aufgeschrieben.

Nicht dazu gehört der Fall „mehrere Benutzer mit unterschiedlichen Rechten“. Das ist Rollen- und Rechteverwaltung und deutlich einfacher. Mandantenfähigkeit heißt: Zwei Nutzer stellen dieselbe Abfrage und bekommen zu Recht völlig verschiedene Daten.

Eine Datenbank oder viele? Die Entscheidung mit Konsequenzen

Beide Wege sind in Laravel gangbar, und beide gängigen Pakete – stancl/tenancy und spatie/laravel-multitenancy – unterstützen laut ihrer Dokumentation jeweils beide Varianten. Die Wahl ist also keine Werkzeugfrage, sondern eine Betriebsfrage.

Eine Datenbank, tenant_idEine Datenbank pro Mandant
Trennunglogisch, über Scopes im Codephysisch, über getrennte Datenbanken
Neuen Mandanten anlegenein DatensatzDatenbank anlegen, migrieren, seeden
Migration ausrolleneinmaleinmal pro Mandant, sequenziell
Restore eines Kundenaufwendig: Zeilen aus einem Gesamt-Backup fischeneinfach: eine Datenbank zurückspielen
Auswertung über alleeine Abfrageüber alle Datenbanken schleifen oder separat zusammenführen
Betriebskostenniedrig, eine Datenbanksteigen mit jedem Mandanten (Verbindungen, Backups, Monitoring)
Risiko bei einem Bugfalsch gescoptes Query zeigt fremde Datenein Bug trifft im schlimmsten Fall nur einen Mandanten

Unsere Empfehlung ist unspektakulär: Starte mit einer Datenbank und tenant_id, es sei denn, du hast einen konkreten Grund dagegen. Konkrete Gründe sind zum Beispiel eine vertraglich zugesicherte physische Trennung, ein einzelner Großkunde, dessen Datenmenge alle anderen zusammen übersteigt, oder die Anforderung, einen einzelnen Kunden auf einen Stand von vor drei Tagen zurückzusetzen, ohne alle anderen anzufassen.

Der Mischweg ist übrigens erlaubt und in der Praxis oft der klügste: gemeinsame Datenbank als Regelfall, eigene Datenbank für die wenigen Kunden, die es wirklich brauchen. Beide Pakete kommen damit zurecht, weil der Mandant zur Laufzeit bestimmt und die Datenbankverbindung umgeschaltet wird.

Datentrennung, die auch bei Fehlern hält

Hier entscheidet sich, ob deine Mandantenfähigkeit echt ist oder nur gut gemeint. Der Standardansatz bei der gemeinsamen Datenbank ist ein Global Scope: eine Bedingung, die Laravel automatisch an jede Eloquent-Abfrage eines Models hängt. stancl/tenancy liefert dafür das Trait BelongsToTenant, das Models auf den aktuellen Mandanten einschränkt; abschalten lässt sich das gezielt über withoutGlobalScope(TenantScope::class).

Das funktioniert gut – hat aber eine Lücke, die man kennen muss:

Global Scopes greifen nur bei Eloquent. Rohes SQL, DB::table() und Pakete, die am Eloquent-Query-Builder vorbeigehen, sehen den Mandanten nicht.

Und genau dort entstehen Datenlecks: im Reporting, im nächtlichen Export, im schnell geschriebenen Join für eine Auswertung. Was dagegen hilft:

  • Keine Ausnahmen bei der Basisklasse. Jedes mandantengebundene Model erbt den Scope, nicht „die meisten“. Ein Model ohne tenant_id ist eine bewusste Entscheidung, die begründet gehört.
  • Policies zusätzlich zum Scope. Der Scope filtert Listen, die Policy prüft den Einzelzugriff. Wer eine ID rät und /rechnungen/4711 aufruft, muss ein 404 bekommen, kein fremdes Dokument.
  • Rohe Abfragen an einer Stelle bündeln. Reporting-Queries gehören in eigene Klassen, in denen der Mandantenfilter explizit gesetzt und geprüft wird – nicht verstreut in Controllern.
  • Row Level Security als zweites Netz. PostgreSQL kann den Mandantenfilter in der Datenbank selbst erzwingen. Dann blockt die Datenbank auch das, was am Code vorbeiläuft. Aufwand: spürbar. Für Anwendungen mit sensiblen Daten trotzdem die ehrlichste Absicherung.

Top tip

Schreib einen Test, der versucht, was nicht gehen darf: als Mandant A eingeloggt jede ID von Mandant B abrufen, listen, exportieren, ändern – erwartet wird 404 oder eine leere Liste. Dieser eine Test fängt später mehr ab als jede Code-Review, weil er bei jedem neuen Endpunkt mitläuft. Ohne ihn merkst du das Leck erst, wenn ein Kunde den Firmennamen eines anderen in seinem Export liest.

Migrationen über alle Mandanten: der Punkt, an dem es wehtut

Bei einer gemeinsamen Datenbank ist eine Migration eine Migration. Bei getrennten Datenbanken ist sie eine Schleife – und Schleifen können abbrechen.

Beide Pakete bringen dafür Werkzeuge mit. stancl/tenancy hat ein eigenes Kommando tenants:migrate (plus tenants:seed), das die Mandanten-Datenbanken migriert; die Migrationen liegen dort standardmäßig in database/migrations/tenant, und einzelne Mandanten lassen sich über --tenants= gezielt ansprechen. spatie/laravel-multitenancy geht den allgemeineren Weg: tenants:artisan macht jeden Mandanten nacheinander zum aktuellen und führt ein beliebiges Artisan-Kommando aus, zum Beispiel:

php artisan tenants:artisan "migrate --database=tenant --force"

Was du dir dabei einhandelst:

  • Der Lauf ist nicht atomar. Bei 80 Mandanten kann Nummer 43 fehlschlagen – dann hast du 42 Mandanten auf dem neuen und 38 auf dem alten Stand, während der neue Code schon deployt ist. Migrationen müssen deshalb rückwärtskompatibel sein: erst Spalte hinzufügen, dann Code ausrollen, dann alte Spalte entfernen. Nie in einem Schritt.
  • Die Dauer wächst linear. Zehn Sekunden pro Mandant sind bei fünf Kunden nichts und bei dreihundert eine Dreiviertelstunde Deployment.
  • Neue Mandanten brauchen einen sauberen Startzustand. Datenbank anlegen, migrieren, Grunddaten seeden – das muss ein Job erledigen, kein Mensch. Sonst ist der zwanzigste Mandant anders aufgesetzt als der erste.

Bei der gemeinsamen Datenbank verschiebt sich das Problem nur, es verschwindet nicht: Dort ist die Migration einmal fällig, läuft aber über die Tabelle aller Kunden. Eine unbedachte Änderung an einer Tabelle mit Millionen Zeilen sperrt sie – und damit alle Mandanten gleichzeitig.

Backup und Restore für einen einzelnen Kunden

Diese Frage kommt selten im Konzept vor und immer im Ernstfall: Ein Kunde hat sich seine Daten zerschossen und will den Stand von gestern zurück – die anderen 79 Mandanten aber nicht.

Mit einer Datenbank pro Mandant ist das ein kurzer Vorgang: die eine Datenbank aus dem Backup zurückspielen, fertig. Mit einer gemeinsamen Datenbank ist es echte Arbeit: Backup in eine temporäre Datenbank einspielen, die Zeilen des Mandanten über alle betroffenen Tabellen herausziehen, Fremdschlüssel-Reihenfolge beachten, in die Live-Datenbank zurückschreiben. Machbar, aber ein halber Tag statt zehn Minuten.

Wenn du weißt, dass dieser Fall bei dir vorkommen wird – regulierte Branchen, große Einzelkunden, Datenimporte, die schiefgehen können –, ist das ein starkes Argument für getrennte Datenbanken. Wenn nicht, ist es ein theoretisches Risiko, für das du dauerhaft Betriebskomplexität bezahlst.

Was die Pakete abnehmen – und was nicht

Kurz zur Einordnung, weil beide Namen bei jeder Recherche auftauchen:

  • stancl/tenancy unterstützt Single- und Multi-Database-Tenancy, legt den Schwerpunkt laut eigener Doku aber auf den Multi-Database-Fall, weil dort mehr Arbeit im Paket steckt und weniger bei dir. Für die gemeinsame Datenbank liefert es Model-Scopes und Traits.
  • spatie/laravel-multitenancy beschreibt sich selbst als bewusst minimal – „bare essentials“ – und arbeitet mit austauschbaren Tasks: Alles, was beim Wechsel des aktuellen Mandanten passieren soll, ist eine Klasse, die SwitchTenantTask implementiert. Bei getrennten Datenbanken hältst du zwei Verbindungen: landlord für die mandantenübergreifenden Daten und tenant für den aktuellen Mandanten.

Was dir kein Paket abnimmt: die Entscheidung, welche Daten überhaupt mandantengebunden sind. Nutzerkonten, die zu mehreren Mandanten gehören können. Dateien und Uploads, die auch getrennt liegen müssen. Caches und Queues, die den Mandanten mitführen müssen – ein Job, der ohne Mandanten-Kontext in der Warteschlange landet, schreibt im Zweifel beim Falschen. Genau an diesen Rändern entstehen die Fehler, nicht in der Datenbankwahl.

Nachträglich mandantenfähig machen: was das kostet

Der häufigste Fall in der Praxis ist nicht die grüne Wiese, sondern eine laufende Anwendung, die einen zweiten Kunden bekommen soll. Das ist Umbau am fahrenden System und läuft in vier Etappen:

  1. Inventur. Welche Tabellen sind mandantengebunden, welche global? Diese Liste ist die eigentliche Arbeit – und sie ist in ein, zwei Tagen zu haben.
  2. Schema und Scopes. tenant_id ergänzen, bestehende Daten dem ersten Mandanten zuordnen, Scopes und Policies einziehen, Tests schreiben, die die Trennung beweisen.
  3. Die Ränder. Jobs, Caches, Dateiablage, Exporte, Mails, geplante Aufgaben – überall muss der Mandanten-Kontext ankommen.
  4. Betrieb. Anlage neuer Mandanten automatisieren, Monitoring pro Mandant, Migrationsstrategie festlegen.

Wir rechnen so etwas nach Aufwand zu 79 €/h ab; ein klar abgegrenztes Vorprojekt – Inventur plus Umbau eines Bereichs als Muster für den Rest – starten wir ab 3.900 €. Das ist derselbe Rahmen wie bei anderen Laravel-Entwicklungsprojekten. Wichtig ist die Reihenfolge: Erst die Inventur, dann das Angebot. Wer den Umbau ohne diese Liste pauschal anbietet, rät.

Und ja, es gibt den Fall, in dem wir davon abraten: Wenn du genau zwei Kunden haben wirst und beide sehr unterschiedliche Anforderungen mitbringen, sind zwei saubere Installationen manchmal ehrlicher als ein Mandanten-System, das keiner der beiden richtig passt. Die laufende Pflege beider Installationen musst du dann allerdings einplanen – was das über die Jahre bedeutet, steht in unserem Artikel zu Laravel-Wartung und Upgrades.

Du hast eine Laravel-Anwendung, die einen zweiten oder zehnten Mandanten bekommen soll – und willst wissen, ob eine Datenbank reicht oder du getrennte brauchst? Schick uns kurz, wie viele Mandanten realistisch dazukommen und ob jemand physische Trennung verlangt. Wir schauen im kostenlosen Erstgespräch gemeinsam drauf und sagen dir ehrlich, welcher Weg zu deiner Anwendung passt – und was der Umbau ungefähr kostet. Wie wir Laravel-Anwendungen entwickeln und warum wir auf dieses Framework setzen, kannst du dir vorab ansehen: Warum wir auf Laravel setzen.

Häufige Fragen

Was bedeutet Mandantenfähigkeit in einer Laravel-Anwendung?

Mehrere Kunden, Standorte oder Organisationen nutzen dieselbe Anwendung, sehen aber jeweils nur ihre eigenen Daten. Technisch heißt das: Jede Abfrage wird automatisch auf den aktuellen Mandanten eingeschränkt – entweder über eine Spalte tenant_id in einer gemeinsamen Datenbank oder über eine eigene Datenbank je Mandant. Das ist mehr als Rollen und Rechte: Zwei Nutzer stellen dieselbe Abfrage und bekommen zu Recht völlig verschiedene Daten.

Eine Datenbank mit tenant_id oder eine Datenbank pro Mandant – was ist besser?

Für die meisten Anwendungen ist eine gemeinsame Datenbank mit tenant_id die richtige Wahl: einfacher zu betreiben, günstiger, schneller zu ändern. Eine eigene Datenbank pro Mandant lohnt sich bei vertraglich zugesicherter physischer Trennung, bei sehr großen Einzelkunden oder wenn du einen einzelnen Kunden aus dem Backup wiederherstellen können musst. Ein Mischbetrieb ist möglich: gemeinsame Datenbank als Regelfall, eigene Datenbank für die wenigen Kunden, die sie brauchen.

Welches Laravel-Paket eignet sich für Multi-Tenancy?

Verbreitet sind stancl/tenancy und spatie/laravel-multitenancy. Beide unterstützen laut ihrer Dokumentation sowohl eine gemeinsame als auch getrennte Datenbanken. stancl/tenancy legt den Schwerpunkt auf den Multi-Database-Fall und bringt für die gemeinsame Datenbank Model-Scopes und Traits mit; spatie/laravel-multitenancy ist bewusst minimal gehalten und arbeitet mit austauschbaren Tasks beim Mandantenwechsel. Die Paketwahl ist die kleinere Entscheidung – die größere ist eine oder viele Datenbanken.

Wie stelle ich sicher, dass kein Mandant fremde Daten sieht?

Mit drei Schichten. Erstens Global Scopes, die jede Eloquent-Abfrage automatisch auf den Mandanten einschränken. Zweitens Policies für den Einzelzugriff, damit eine geratene ID ein 404 ergibt und kein fremdes Dokument. Drittens automatisierte Tests, die den Zugriff über Mandantengrenzen hinweg aktiv versuchen und fehlschlagen müssen. Wichtig zu wissen: Global Scopes greifen nur bei Eloquent – rohes SQL und DB::table() umgehen sie. Bei sensiblen Daten ist Row Level Security in PostgreSQL das zweite Netz.

Wie laufen Migrationen, wenn jeder Mandant eine eigene Datenbank hat?

Nacheinander über alle Mandanten. stancl/tenancy bringt dafür tenants:migrate mit, spatie/laravel-multitenancy das allgemeinere tenants:artisan, das ein beliebiges Artisan-Kommando je Mandant ausführt. Der Lauf ist nicht atomar: Er kann bei Mandant 43 von 80 abbrechen. Migrationen müssen deshalb rückwärtskompatibel sein – erst Spalte hinzufügen, dann Code ausrollen, dann Altes entfernen.

Kann man eine bestehende Laravel-Anwendung nachträglich mandantenfähig machen?

Ja, aber es ist Umbau am fahrenden System. Der Ablauf: Inventur (welche Tabellen sind mandantengebunden), Schema und Scopes, dann die Ränder – Jobs, Caches, Dateiablage, Exporte, Mails –, zuletzt der Betrieb mit automatischer Mandantenanlage. Wir rechnen das nach Aufwand zu 79 €/h ab; ein abgegrenztes Vorprojekt aus Inventur und einem umgebauten Beispielbereich startet ab 3.900 €.

Weitere Artikel

Laravel-Agentur wechseln: So übernimmt ein neues Team dein Projekt

Die Laravel-Agentur wechseln heißt übergeben, nicht neu bauen: Die Einarbeitung dauert wenige Tage bis zwei Wochen. So läuft eine saubere Übernahme ab.

Weiterlesen

Warum wir auf Laravel setzen – und wann es die richtige Wahl ist

Laravel löst die Probleme, die jedes Projekt hat: Login, Rechte, Warteschlangen, Tests. Deshalb bauen wir Backends und APIs damit – ab 3.900 €.

Weiterlesen

Laravel und Statamic für eine große Content-Website: Erfahrungsbericht aus 309 Seiten

Statamic auf Laravel für eine inhaltsschwere Website mit 309 Seiten, 14 Rechnern und 26 Anbieterprofilen: Was daran technisch wirklich anspruchsvoll ist – und wann sich die Kombination lohnt.

Weiterlesen

Laravel vs. WordPress: Wann brauchst du welches?

WordPress ist ein fertiges CMS für Inhalte, Laravel ein Framework für eigene Anwendungen. Wir vergleichen Sicherheit, Performance und Kosten – mit Empfehlung.

Weiterlesen

Erzähl uns von deinem Projekt

Kostenloses Erstgespräch, unverbindlich – wir melden uns persönlich und kurzfristig.