Tu magasines Supabase pour ton prochain projet et tu veux savoir combien de trafic ça peut vraiment absorber. C’est la bonne question à poser avant de construire, pas après avoir signé avec tes premiers clients. Voici les chiffres bruts, ce qu’ils veulent dire concrètement, et où ça craque. Ce que tu vas apprendre :
- Comment les connexions simultanées sont distribuées selon le palier de compute
- Le rôle exact de PgBouncer dans la capacité réelle de ton app
- Les chiffres de débit par instance : Small, Medium, Large
- Les facteurs qui plombent le débit théorique en production
- Quand Supabase n’est plus suffisant et quoi faire
Ce qu’on entend par capacité de trafic concurrent en serverless
Quand on parle de « requêtes par seconde », il faut d’abord comprendre ce qu’on mesure. Une requête HTTP qui frappe ton API, ça peut être une lecture simple d’un profil utilisateur ou une transaction qui touche cinq tables. Le chiffre de débit ne veut rien dire sans le contexte de la complexité des queries. Le modèle serverless de Supabase ajoute une couche de complexité. Ton backend peut scaler horizontalement (plusieurs instances de ta fonction, plusieurs conteneurs), mais ta base de données, elle, reste sur une seule instance Postgres. C’est là que le goulot se forme.
Le concept critique ici : le nombre de connexions simultanées que Postgres peut maintenir est une ressource finie. Chaque connexion ouverte consomme de la RAM, que la connexion soit active ou non. Ouvrir mille connexions inactives sur une petite instance, c’est la façon la plus rapide de la faire manquer de mémoire.
À retenir : En serverless, tu ne contrôles pas le nombre de connexions que tes fonctions ouvrent. Sans pooling, une app à volume modéré peut épuiser les connexions disponibles en quelques minutes de pic de trafic.
Architecture de Supabase : connexions, PgBouncer et limites par palier
Supabase intègre PgBouncer comme pooler de connexions entre ton application et Postgres. PgBouncer maintient un pool de connexions persistantes vers Postgres, et tes clients se connectent à lui plutôt qu’à Postgres directement.
PgBouncer a trois modes de fonctionnement, et le mode choisi change complètement le comportement sous charge :
- Session pooling : Une connexion Postgres est réservée pour toute la durée de la session client. Pratiquement inutile pour le serverless.
- Transaction pooling : Une connexion est attribuée pour la durée d’une transaction, puis retournée au pool. C’est le mode recommandé pour Supabase avec des functions serverless.
- Statement pooling : Connexion allouée par statement individuel. Incompatible avec les transactions multi-statements. Le mode transaction pooling est le mode optimal pour maximiser le débit. Sauf qu’il a une contrainte : les prepared statements côté client ne fonctionnent pas. C’est pourquoi certains ORM (Prisma en mode natif, par exemple) nécessitent une configuration supplémentaire pour fonctionner correctement avec PgBouncer.
Ce qui varie entre les paliers de compute, c’est précisément le pool_size (connexions réelles vers Postgres) et le max_client_conn (nombre maximum de clients qui peuvent se connecter à PgBouncer simultanément). Ces deux chiffres déterminent ta capacité réelle.
Les chiffres bruts : requêtes par seconde selon la taille d’instance
Voici les données vérifiées par palier de compute :
| Instance | Pool Size (connexions Postgres) | Max Client Connections | Débit estimé |
|---|---|---|---|
| Small | 15 connexions | 400 connexions | ~300 queries/sec |
| Medium | 50 connexions | 1 000 connexions | ~1 000 queries/sec |
| Large | 100 connexions | 2 000 connexions | ~2 000 queries/sec |
Ces chiffres sont des estimations basées sur des queries simples : des SELECT sur des tables indexées, latence réseau locale. En production, avec des jointures, des agrégations ou des requêtes non optimisées, ce chiffre descend. L’instance Small avec ses 15 connexions réelles vers Postgres peut encore servir 400 clients simultanés via PgBouncer en mode transaction pooling. Le pooler fait le travail de multiplexage : 400 clients qui attendent, 15 qui travaillent activement dans Postgres.
Le truc c’est que ces 15 connexions suffisent si tes queries sont rapides. Une query qui prend quelques millisecondes libère sa connexion Postgres presque immédiatement. Mais si tes queries prennent des secondes, tu bloques le pool.
Le chiffre de ~300 queries/sec pour la Small implique des queries très courtes. C’est réaliste pour une API de lecture de données simples, un endpoint de vérification d’authentification, ou une recherche sur une table bien indexée.
Instance Large à 110 $/mois : plafond réel et cas d’usage adaptés
L’instance Large coûte 0,1517 $/heure (~110 $/mois). Pour ce prix, tu obtiens 100 connexions réelles vers Postgres, 2 000 connexions clients maximum via PgBouncer, et un débit estimé de ~2 000 queries/sec.
C’est adapté pour quel type d’app? Un SaaS B2B avec quelques milliers d’utilisateurs actifs, une app mobile avec des pics de trafic raisonnables, ou une API interne qui gère du volume sans transactions ultra-complexes. Pas pour une app de financement participatif qui passe à la télé et reçoit dix mille inscriptions en vingt minutes. Ce que l’instance Large ajoute aussi : la possibilité d’ajouter jusqu’à 5 replicas en lecture. Ça, c’est majeur pour les apps à lecture intensive. Si la majorité de tes requêtes sont des SELECT (typique pour beaucoup d’apps web), tu peux répartir la charge de lecture sur plusieurs replicas et réserver l’instance principale pour les écritures.
Avec 5 replicas en lecture, ton débit de lecture effectif peut être multiplié plusieurs fois par rapport au débit d’une seule instance. Le chiffre de ~2 000 queries/sec devient la capacité d’écriture et de lecture non répliquée, pas le plafond absolu de l’architecture. La latence REST API round-trip est de 100 à 150 ms depuis les États-Unis pour un SELECT simple. Depuis le Canada, ajoute quelques dizaines de millisecondes selon la région de ton instance. Ce chiffre est important : il plafonne mécaniquement ton débit par connexion persistante. Une connexion ne peut pas faire plus d’une certaine quantité de requêtes séquentielles par seconde si chaque requête prend cette durée.
Comparaison des paliers et facteurs qui réduisent le débit réel
Regardons aussi les plans tarifaires de Supabase pour comprendre dans quel contexte tu accèdes à quel compute :
| Plan | Prix | Accès compute custom |
|---|---|---|
| Free | 0 $US/mois | Compute Nano seulement |
| Pro | 25 $US/mois | Compute upgradeable en option |
| Team | 599 $US/mois | Compute upgradeable, SLA inclus |
| Enterprise | Négocié | Compute dédié et custom |
Le plan Free te donne accès à l’instance Nano, qui n’est pas dans le tableau de débit ci-dessus. Considère-la comme en-dessous de la Small en termes de ressources. C’est bien pour du développement et du prototypage, pas pour de la production avec des utilisateurs réels.
À noter : La progression du débit entre les paliers n’est pas linéaire dans le sens des coûts. Passer de Small à Medium triple approximativement le débit en multipliant le coût par moins que trois. C’est souvent Medium qui représente le meilleur équilibre pour une app en croissance. Maintenant, les facteurs qui réduisent ce débit en production : Les queries lourdes. Une query avec plusieurs JOIN, une agrégation sur une grande table non partitionnée, ou un LIKE sans index trigram peut tenir une connexion Postgres pendant des secondes. Pendant ce temps, cette connexion est bloquée. Quinze connexions occupées pendant plusieurs secondes chacune, c’est ton pool Large qui est épuisé. Les transactions longues. En mode transaction pooling, une connexion Postgres est occupée pour toute la durée de ta transaction, pas juste pour chaque statement individuel. Une transaction qui fait plusieurs opérations en séquence avec des appels API entre les deux (jamais une bonne idée, mais ça arrive) monopolise une connexion. Les migrations en production. Quand Supabase ou toi-même exécutez une migration qui acquiert un lock sur une table populaire, toutes les requêtes vers cette table attendent. En dehors des heures de pointe, c’est gérable. En plein pic, c’est une coupure.
Le cold start des Edge Functions. Les fonctions serverless de Supabase (Edge Functions) ont un temps de démarrage à froid. Le premier appel après une période d’inactivité prend plus longtemps. Ça n’affecte pas directement les connexions Postgres, mais ça allonge la latence perçue par le client et peut empiler les requêtes en attente. Les connexions directes non-poolées. Supabase donne accès à un port de connexion directe à Postgres, en plus du port PgBouncer. Certains outils (migrations, scripts batch, certains ORM mal configurés) utilisent ce port direct. Chaque connexion directe compte sur les connexions totales de Postgres et bypasse le pooler. Si ton script de migration ouvre dix connexions directes sur une Small, tu manges une proportion significative de tes connexions disponibles.
Quand Supabase devient le goulot d’étranglement
Supabase ne te prévient pas automatiquement quand tu approches tes limites. Il y a quelques signaux à surveiller.
Le premier : les erreurs too many connections. C’est Postgres qui refuse de nouvelles connexions. En théorie, PgBouncer devrait absorber ce problème, mais si des connexions directes saturent le pool ou si PgBouncer lui-même est mal configuré, tu vas voir ces erreurs côté application.
Le deuxième : une augmentation soudaine du temps de réponse de tes endpoints. Si une API qui répond normalement en quelques millisecondes commence à prendre beaucoup plus longtemps sous charge, c’est souvent le signe que les connexions PgBouncer font la file. Le client attend qu’une connexion Postgres soit disponible dans le pool.
Le troisième signal : des timeouts côté client. Quand la file d’attente de PgBouncer est pleine (le max_client_conn est atteint), PgBouncer refuse la connexion. Ton client reçoit une erreur plutôt qu’une réponse lente.
Quand tu arrives là, tu as plusieurs options :
Upgrade de compute. La plus simple. Passer de Small à Medium ou de Medium à Large donne plus de connexions Postgres et plus de CPU pour traiter les queries. Ça résout le problème si la limite est sur les connexions ou le CPU.
Ajout de read replicas. Si ton trafic est majoritairement en lecture, un ou plusieurs replicas distribuent la charge. Disponible sur l’instance Large avec jusqu’à 5 replicas.
Optimisation des queries. Souvent l’étape la plus rentable avant un upgrade. Un index manquant sur une colonne de filtre populaire peut transformer une query de plusieurs secondes en quelques millisecondes, libérant les connexions beaucoup plus vite.
Mise en cache applicative. Ajouter Redis (ou Upstash si tu restes dans l’écosystème serverless) pour les données qui changent peu. Les requêtes qui touchent le cache n’atteignent jamais Postgres.
Architecture dédiée. Quand le volume dépasse ce que même une Large avec replicas peut absorber de façon rentable, ou quand tu as besoin d’un contrôle fin de la configuration Postgres, l’auto-hébergement sur un VPS dédié ou une base de données managée plus puissante (RDS, Cloud SQL, Neon avec ses propres caractéristiques) devient pertinent.
Supabase est bâti sur Postgres open-source sous licence Apache 2.0. Ça veut dire que si tu migres vers de l’auto-hébergement, tu emmènes les mêmes données, le même schéma, les mêmes fonctions SQL. La migration est technique mais pas un lock-in complet.
Facteurs architecturaux souvent négligés
Il y a deux aspects que les benchmarks de débit n’incluent généralement pas, mais qui impactent la capacité réelle de ton app en production. Le Row Level Security (RLS). Supabase encourage fortement l’activation du RLS sur toutes les tables. C’est une bonne pratique de sécurité. Mais chaque policy RLS ajoute une condition à chaque requête. Sur des tables avec beaucoup de lignes et des policies complexes, le coût CPU par query monte. Ton instance peut atteindre sa limite CPU avant d’atteindre sa limite de connexions. La taille de tes payloads REST. La latence REST API round-trip de 100 à 150 ms depuis les États-Unis s’applique à des SELECT simples retournant peu de données. Si ton endpoint retourne des centaines de lignes avec des champs volumineux (texte long, JSON imbriqué), le temps de sérialisation et de transmission s’ajoute à la latence de la base de données. Le débit effectif en termes de requêtes par seconde pour ces endpoints va être significativement plus bas.
Un anti-pattern fréquent : utiliser l’API REST Supabase pour charger des listes complètes côté client et filtrer en JavaScript. Chaque appel charge des centaines de lignes que le client ignore. Non seulement c’est lent, mais ça consomme des connexions et de la bande passante inutilement. Filtrer côté base de données, paginer, retourner seulement les colonnes nécessaires : c’est la base de l’optimisation. L’utilisation des Realtime subscriptions. Les abonnements Realtime de Supabase maintiennent des websockets ouverts. Chaque abonnement actif a un coût en ressources côté serveur. Pour des apps avec beaucoup d’utilisateurs actifs simultanément qui utilisent tous le Realtime, ce coût s’accumule et peut affecter la capacité globale indépendamment du compute choisi.
Conclusion : Supabase convient à quel volume de trafic
Soyons clairs. Supabase est un excellent choix pour une large gamme d’applications web et mobile, à condition de comprendre ses paliers. L’instance Small à budget minimal gère une app avec un volume de trafic modéré sur des queries simples et bien indexées. C’est suffisant pour un outil interne, un SaaS en early access, ou une app hobby qui commence à avoir de vrais utilisateurs. L’instance Medium est le bon palier pour la plupart des SaaS B2B avec quelques milliers d’utilisateurs actifs. ~1 000 queries par seconde, c’est déjà beaucoup pour une app qui n’est pas encore à scale significatif. La grande majorité des apps n’atteignent jamais ce plafond. L’instance Large à 0,1517 $/heure (~110 $/mois) avec ses ~2 000 queries par seconde et ses 5 replicas en lecture possibles couvre une très large gamme d’applications web sans nécessiter d’infrastructure supplémentaire. C’est le plafond naturel de Supabase managé avant de considérer une architecture plus complexe.
| Palier | Adapté pour | Pas adapté pour |
|---|---|---|
| Small | Dev, early access, outils internes | Apps avec pics de trafic imprévisibles |
| Medium | SaaS B2B, app mobile < quelques milliers d'actifs | Charge transactionnelle intensive |
| Large | SaaS en croissance, apps à lecture intensive avec replicas | Volumes massifs, transactions très complexes |
| Auto-hébergé | Volume extrême, contrôle total requis | Équipes sans expertise ops Postgres |
Ce que Supabase ne remplace pas : une architecture pensée. Peu importe le palier, si tes queries ne sont pas optimisées, si tu ouvres des connexions directes à côté du pooler, si tes transactions sont trop longues, tu vas saturer l’instance avant d’atteindre son plafond théorique.
Le verdict de fond : Supabase est fondé en 2020, passé par Y Combinator, bâti sur Postgres open-source. Ce n’est pas un outil jouet. C’est une infrastructure sérieuse avec des limites documentées et prévisibles. Ces limites correspondent à ce que la plupart des startups et des équipes produit vont rencontrer seulement après avoir acquis un volume d’utilisateurs réel. La vraie question n’est pas « est-ce que Supabase peut gérer mon trafic? ». La vraie question est : « est-ce que mon code est assez efficace pour que Supabase n’ait pas à compenser mes mauvaises requêtes? » Commence par le plan Pro à 25 $US/mois avec l’instance Small. Surveille les métriques. Optimise tes queries. Quand tu approches des limites, upgrade de compute. Ce n’est pas plus compliqué que ça.
Les chiffres de débit et de connexions cités dans cet article sont basés sur les données documentées de Supabase. Les chiffres de production réels varient selon la complexité des queries, l’utilisation du RLS, et la configuration applicative.
Check tes courriels.
Lien à cliquer pour confirmer ton abonnement.
Texte par David Cyr
