En bref — héberger chaque projet sur son propre serveur coûte cher et gaspille des ressources ; tout empiler pêle-mêle au même endroit est un risque. Entre les deux, il y a la mutualisation : partager l'infrastructure coûteuse — base de données, cache, moteur de recherche — tout en cloisonnant chaque projet si strictement qu'aucun ne peut lire les données d'un autre ni asphyxier la machine. Voici comment je m'y prends, brique par brique, et la règle qui rend l'exercice tenable : garder à tout instant la porte de sortie ouverte.
Quand on héberge plusieurs projets personnels ou en phase de démarrage, on se heurte vite à un dilemme. Donner à chacun son propre serveur, avec sa base de données et son cache dédiés, c'est propre — mais c'est aussi payer trois, cinq, dix fois une infrastructure qui, sur des projets sans trafic sérieux, passe l'essentiel de son temps à ne rien faire. À l'inverse, tout entasser sur une seule machine sans discipline, c'est ouvrir la porte au pire : un projet qui fuite les données d'un autre, ou qui part en vrille et emporte tous ses voisins avec lui.
La mutualisation est la troisième voie. Son principe tient en une phrase : mutualiser ce qui coûte cher, isoler tout le reste. Une seule instance PostgreSQL, un seul cache, un seul moteur de recherche, partagés entre tous les projets — mais à l'intérieur, des cloisons étanches. Détaillons ces cloisons, parce que c'est là que tout se joue.
PostgreSQL : une base et un utilisateur par projet
Le serveur PostgreSQL est unique, mais chaque projet y possède sa propre base de données et son propre utilisateur, dont les droits s'arrêtent net aux frontières de cette base. Concrètement, l'ajout d'un projet crée un couple dédié :
CREATE DATABASE monprojet_prod;
CREATE USER monprojet WITH PASSWORD '...';
GRANT ALL PRIVILEGES ON DATABASE monprojet_prod TO monprojet;
REVOKE ALL ON DATABASE monprojet_prod FROM PUBLIC;L'utilisateur monprojet peut tout faire dans monprojet_prod, et rien ailleurs. Il ne peut pas se connecter aux bases de ses voisins, ni même en soupçonner l'existence. Ce REVOKE … FROM PUBLIC est le détail qui compte : par défaut, PostgreSQL autorise tout rôle à se connecter à une base fraîchement créée — on referme explicitement cette porte. C'est exactement l'isolation qu'on met en place dans une architecture multi-tenant, mais transposée un cran plus bas : au niveau du serveur, pas de l'application.
Le cache : des préfixes et des permissions, pas une instance par projet
Pour le cache — j'utilise Valkey, le fork libre de Redis né après son changement de licence — la même logique s'applique, avec ses propres outils. Une instance unique, mais chaque projet écrit ses clés sous son préfixe réservé : monprojet:session:…, monprojet:cache:…. Deux projets ne peuvent pas se marcher dessus, même s'ils nomment leurs clés de façon identique.
Le préfixe seul relève de la convention ; pour le rendre contraignant, Valkey applique une liste de contrôle d'accès. Chaque projet reçoit un utilisateur dont les permissions sont littéralement bornées à son préfixe :
user monprojet on >motdepasse ~monprojet:* +@all -@dangerousTraduit : cet utilisateur ne voit que les clés commençant par monprojet:, et les commandes destructrices à l'échelle du serveur — vider la base, lister toutes les clés, reconfigurer le moteur — lui sont interdites. Surtout, les connexions anonymes sont refusées : sans identifiant valide, on n'entre pas. Le cache mutualisé cesse d'être un grand espace ouvert où chacun se sert ; il devient un immeuble à cases fermées.
Docker : plafonner les ressources pour tuer l'effet « voisin bruyant »
L'isolation des données ne suffit pas. Reste le risque le plus insidieux de la mutualisation : le voisin bruyant. Un projet qui, à cause d'un bug ou d'un pic de charge, se met à dévorer toute la mémoire ou tout le processeur — et fait tomber la machine entière, ses voisins innocents compris. Sur un serveur partagé, un plantage n'est jamais un incident local.
La parade tient dans chaque configuration Docker : aucun conteneur ne tourne sans plafond.
services:
app:
mem_limit: 512m
cpus: 1.0
restart: unless-stoppedUne limite de mémoire, une limite de processeur. Si le projet dépasse son enveloppe mémoire, le noyau ne tue que lui — pas la machine. S'il s'emballe côté calcul, il est bridé à sa part et les autres continuent de respirer. C'est la règle sur laquelle je ne transige jamais : un conteneur sans mem_limit, c'est une grenade dégoupillée posée sur le serveur. La mutualisation impose une discipline que l'hébergement isolé, lui, pardonne.
Le réseau : rien de partagé n'est exposé
Dernier étage de l'isolation, et pas le moindre : les services mutualisés ne sont accessibles que de l'intérieur. PostgreSQL, le cache, le moteur de recherche n'ont aucun port ouvert sur le monde extérieur. Ils vivent sur un réseau Docker privé, et les applications les joignent par leur nom de service interne — postgres, valkey — jamais par une adresse publique. Seul le reverse proxy, en façade, est exposé ; il n'ouvre que les ports du web, 80 et 443, et distribue le trafic vers la bonne application selon le domaine.
Ce cloisonnement réseau est ce qui rend les couches précédentes crédibles. Un mot de passe de base de données ne vaut que si personne, depuis Internet, ne peut seulement tenter de s'y connecter. En gardant les services partagés hors de portée du réseau public, on réduit la surface d'attaque au strict minimum : une seule porte d'entrée, surveillée, plutôt qu'une façade criblée d'ouvertures.
La règle d'or : garder la porte de sortie ouverte
Voilà ce qui, à mes yeux, rend toute cette mutualisation acceptable plutôt que subie : chaque projet doit pouvoir partir seul, du jour au lendemain. Parce qu'un projet mutualisé aujourd'hui est un projet qui, s'il décolle, devra un jour voler de ses propres ailes sur son serveur dédié.
Cette migration doit rester triviale, et elle l'est par construction. Chaque projet est déjà autonome : sa base de données isolée se copie d'un dump et se restaure ailleurs, ses fichiers uploadés vivent dans leur propre espace, sa configuration tient dans un fichier d'environnement. Déménager un projet, c'est extraire sa base, ses volumes et son .env, les poser sur une nouvelle machine et repointer le domaine. Aucune donnée emmêlée avec celle des voisins, aucun câble à démêler — précisément parce qu'on a pris soin de ne rien emmêler dès le départ.
C'est le paradoxe fécond de la mutualisation bien faite : on partage l'infrastructure, mais on architecture chaque projet comme s'il était déjà seul. L'isolation n'est pas seulement une mesure de sécurité — c'est aussi la garantie qu'on ne se piège pas soi-même.
Ce que ça coûte, et quand s'en abstenir
Soyons honnête : la mutualisation n'est pas gratuite en effort. Elle exige une rigueur constante — chaque nouveau projet impose de créer son utilisateur de base, son préfixe de cache, ses permissions, ses limites de conteneur. C'est une discipline, pas un réglage qu'on active une fois pour toutes. Et elle a ses bornes : deux projets qui exigent des versions incompatibles de PostgreSQL ne peuvent pas partager la même instance ; un projet à fort trafic finira par mériter sa propre machine plutôt que de rançonner les ressources communes.
Mais pour une constellation de projets personnels et en phase de démarrage — sans trafic sérieux, sans budget d'infrastructure démesuré — l'équation est imbattable. Une seule machine bien tenue porte une dizaine de projets pour le prix d'un café par mois et par projet, chacun cloisonné, sauvegardé, et libre de partir le jour où il grandit. Mutualiser, ce n'est pas héberger au rabais : c'est refuser de payer dix fois pour du vide, sans jamais renoncer à l'isolation.