Architecture multi-tenant SaaS PostgreSQL row‑level security : guide technique pour CTO
15/04/2026
architecture multi-tenant SaaSQL PostgreSQL row-level security
Pour un SaaS, isoler les données des clients (tenants) est à la fois une exigence fonctionnelle et de sécurité. Ce guide technique explique comment concevoir une architecture multi‑tenant avec PostgreSQL en s'appuyant sur le mécanisme Row‑Level Security (RLS). Public : CTO, lead dev, architecte backend. À la fin vous saurez choisir une stratégie de tenancy, implémenter des politiques RLS robustes, et éviter les erreurs courantes.
1. choix d'une stratégie de tenancy (aperçu rapide)
Trois approches courantes :
- Shared database / shared schema : une table centralisée avec une colonne tenant_id. Avantages : déploiement simple, faible coût. Inconvénients : isolement logique uniquement, complexité des migrations pour très grand nombre de tenants.
- Shared database / separate schema : un schéma par tenant. Meilleure isolation logique, migrations par schéma possibles. Plus complexe à gérer à grande échelle.
- Database per tenant : isolation maximale, simplicité réglementaire; coût et orchestration plus élevés (sauvegarde, scaling, connexions).
Choix pratique : commencer en shared schema si vous visez rapidité et faible coût, puis migrer si le besoin d'isolation ou conformité l'exige.
2. principe retenu ici
Nous utilisons la stratégie shared database / shared schema + PostgreSQL RLS pour assurer l'isolement par tenant. Cette approche est scalable et économiquement intéressante pour des centaines à quelques milliers de tenants si elle est correctement construite.
3. implémentation pas à pas (exemples concrets)
3.1 modèle de données minimal
CREATE TABLE tenants (
id TEXT PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE invoices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES tenants(id),
amount NUMERIC,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
3.2 activer RLS et créer une policy
RLS doit être activé table par table. La policy compare tenant_id à une variable de session PostgreSQL.
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY invoices_isolation ON invoices
USING ( tenant_id = current_setting('app.tenant', true) );
Note : current_setting('app.tenant', true) retourne NULL si la variable n'est pas définie (selon la documentation PostgreSQL). Pour les comparaisons vous pouvez caster le type si nécessaire : tenant_id::text = current_setting('app.tenant', true). Voir la doc officielle sur RLS pour plus de détails : PostgreSQL - Row Level Security.
3.3 définir la variable de session depuis votre application
Important : définir la variable à l'intérieur de la transaction ou en SET LOCAL pour éviter les fuites entre requêtes.
// exemple Node.js avec node-postgres (pg)
await client.query('BEGIN');
await client.query("SET LOCAL app.tenant = $1", [tenantId]);
const res = await client.query('SELECT * FROM invoices WHERE created_at > now() - interval '30 days'');
await client.query('COMMIT');
Alternativement, utilisez SET LOCAL suivi des requêtes dans la même transaction. Cela évite d'oublier de réinitialiser la variable.
4. erreurs fréquentes et pièges
- Oublier de définir la variable de session : provoque des résultats vides si la policy compare à NULL. Testez systématiquement l'absence de variable.
- Fonctions SECURITY DEFINER : une fonction définie SECURITY DEFINER s'exécute avec les droits du propriétaire et peut contourner RLS. Évitez ou ajoutez des contrôles explicites dans la fonction.
- Politiques trop permissives : une policy qui retourne TRUE pour certains rôles annule l'isolation. Vérifiez les conditions et testez avec comptes de test tenants.
- Indexation : filter sur tenant_id doit rester indexé pour éviter full scans.
5. performance et scaling (bons réflexes)
- Utiliser un pooler (PgBouncer, transaction pooling) pour éviter explosion des connexions par tenant. Voir PgBouncer pour les options.
- Indexer systématiquement
tenant_idet les colonnes filtrées. - Surveiller : latence moyenne des requêtes, nombre de connexions actives, taux d'erreurs 5xx. Créez des alertes sur augmentation des temps de lock ou temps de requête.
- Penser shard vertical ou base par tenant si vous atteignez des limites opérationnelles (ex : sauvegardes, tailles de base très inégales).
6. sécurité et conformité
RLS améliore l'isolation mais ne remplace pas une stratégie complète :
- Gérer la séparation des roles PostgreSQL : limiter les droits superuser et n'accorder que SELECT/INSERT/UPDATE nécessaires.
- Chiffrement au repos et en transit (TLS) selon vos obligations réglementaires.
- Auditer les actions sensibles et les accès via logs ou solutions SIEM.
7. tests automatisés recommandés
Avant chaque release :
- Tests d'intégration multi-tenant : vérifier qu'un tenant A n'accède jamais aux données de B.
- Tests de non-régression sur fonctions PL/pgSQL.
- Scénarios de montée en charge : exécuter requêtes concurrentes avec pgbouncer en mode transaction pooling pour valider latence et concurrence.
8. tableau récapitulatif (tradeoffs)
| Approche | Isolation | Coût / Opérations | Quand choisir |
|---|---|---|---|
| Shared schema + RLS | Moyenne (logique) | Faible | Startups, < 10k tenants, coût optimisé |
| Schema par tenant | Bonne | Moyen | Besoin d'isolement modéré, migrations isolées |
| DB par tenant | Excellente | Élevé | Contrainte réglementaire, clients critiques |
9. bonnes pratiques opérationnelles (checklist)
- Mettre en place des tests d'isolation automatisés.
- Utiliser
SET LOCAL app.tenantdans les transactions. - Limiter l'usage de SECURITY DEFINER ou contrôler son comportement.
- Surveiller connexions et latences, utiliser PgBouncer.
- Documenter les politiques RLS pour l'équipe (qui peut créer/modifier quoi).
Conclusion
PostgreSQL RLS, combiné à une stratégie partagée et des bonnes pratiques opérationnelles, permet de construire un SaaS multi‑tenant sûr et performant sans coûts d'isolation extrêmes. Pour les CTO/lead dev : commencez par des policies simples et testables, surveillez la performance et planifiez une stratégie d'évolution (sharding ou DB par tenant) selon la croissance.
Pour aller plus loin, consultez notre page sur PostgreSQL : Novane – PostgreSQL, nos services SaaS : Novane – services SaaS et notre expertise Node.js si vous implémentez côté backend : Novane – Node.js.
Besoin d'un audit architecture ou d'un prototype sécurisé multi‑tenant ? Contactez-nous pour en parler.

