← blog

Supabase « max clients reached in session mode » sur Vercel, et la correction qui a tout aggravé

20 septembre 2026 · 7 min de lecture

L’erreur est (EMAXCONNSESSION) max clients reached in session mode - max clients are limited to pool_size: 15. Notre API a commencé à la renvoyer alors que le site vitrine restait en parfaite santé, car le site vitrine ne touche pas à Postgres. C’est exactement pour cela que ce genre de panne passe inaperçu. Les parties qui ont l’air de bien aller sont celles qui n’ont pas besoin de la base.

Voici ce que signifie l’erreur, les deux erreurs que nous avons faites en la corrigeant, et la chaîne de connexion qui marche vraiment depuis Vercel.

Deux poolers, deux ports

Pourquoi le serverless épuise le mode session

Chaque instance de fonction chaude garde son propre petit pool. Les nôtres en gardaient trois. En mode session, un pool ne rend jamais ses connexions tant que l’instance est chaude. Cinq instances chaudes, c’est quinze connexions, soit le plafond. La sixième requête, d’où qu’elle vienne, échoue.

Un démon de gestion du parc interrogeait trois endpoints toutes les quelques secondes. Il gardait des instances chaudes jour et nuit. Rien d’inhabituel là-dedans. C’est juste une arithmétique que le mode session perd.

Erreur numéro un : corriger d’abord la mauvaise couche

La correction d’urgence a été max: 1 par instance et un délai d’inactivité de vingt secondes, toujours en mode session. Ça a marché. Les instances devenues silencieuses rendaient leur place, et l’API s’est rétablie en quelques minutes. Nous avons ensuite passé la chaîne de connexion en mode transaction, ce qui était la bonne chose à faire. Nous avons laissé max: 1, ce qui ne l’était pas.

Erreur numéro deux : un pool d’une connexion en mode transaction

En mode transaction, une seule connexion par instance fait passer chaque requête de cette instance à la file dans un seul tuyau. Les sondages du démon se sont accumulés derrière. Les requêtes ont dépassé leur délai de vingt secondes, et le symptôme est passé des erreurs 500 aux blocages. Le site vitrine restait rapide. L’API ne répondait plus du tout.

Le mode transaction rend une connexion après chaque transaction. Il lui faut donc un pool de taille normale. Nous avons mis huit, et les dépassements de délai ont cessé.

Comment le voir venir

Le pooler ne vous dit rien jusqu’au moment où il vous refuse. Postgres lui-même vous le dira, si vous le lui demandez.

select application_name, state, count(*)
from pg_stat_activity
where datname = 'postgres'
group by 1, 2
order by 3 desc;

Lancez cela sur une connexion en mode session pendant que l’app est sous charge normale. Si le total grimpe vers le plafond et que la plupart des lignes sont inactives, vous êtes à une minute chargée de l’erreur. En mode transaction, la même requête montre des connexions recyclées au lieu d’être accaparées.

La chaîne de connexion qui marche vraiment

postgresql://postgres.<project-ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres
  • L’hôte est le pooler, pas db.<ref>.supabase.co. Le tableau de bord Supabase peut afficher l’hôte direct sous un titre de pooler en mode transaction. C’est quand même l’hôte direct.
  • Le nom d’utilisateur est postgres.<project-ref> sur le pooler. Un simple postgres ne marche que sur l’hôte direct.
  • L’hôte direct est uniquement en IPv6, sauf si vous achetez l’option IPv4. Il a un enregistrement AAAA et aucun enregistrement A. Les fonctions Vercel ne peuvent pas le joindre, et le port 6543 n’y est de toute façon pas ouvert.
  • Aucune query string nécessaire pour postgres.js. Mettez prepare: false. Le drapeau ?pgbouncer=true est une convention de Prisma.
  • Gardez les migrations sur 5432. Drizzle et les outils similaires veulent une connexion en mode session. Pointez l’app déployée vers 6543, et votre environnement de migration local vers 5432.
$ dig +short A    db.<project-ref>.supabase.co     # (nothing)
$ dig +short AAAA db.<project-ref>.supabase.co     # 2600:1f18:...
$ nc -z aws-0-us-east-1.pooler.supabase.com 6543   # succeeded

Le dimensionnement, en un tableau

Une dernière chose à savoir. Pendant tout cela, le démon qui ne joignait plus l’API a conclu que chaque machine qu’il gérait était morte. Il s’apprêtait à leur couper le courant. Cette histoire est dans comment une limite de connexions à la base de données a failli redémarrer les Mac de mes clients.

Questions

Quel port Supabase une app Vercel doit-elle utiliser ?
6543, le pooler en mode transaction. Le mode session sur 5432 garde une connexion serveur par client et plafonne le projet à 15. Le serverless les épuise vite.
Pourquoi db.<project-ref>.supabase.co ne se connecte-t-il pas depuis Vercel ?
L’hôte direct n’a qu’une adresse IPv6, sauf si vous payez l’option IPv4, et les fonctions Vercel ne le joignent pas en IPv6. Utilisez l’hôte du pooler, aws-0-<region>.pooler.supabase.com.
Quel nom d’utilisateur le pooler attend-il ?
postgres.<project-ref>, avec la référence du projet ajoutée. Un simple postgres ne marche que sur l’hôte direct.
Faut-il ?pgbouncer=true dans la chaîne de connexion ?
Pour Prisma, oui. Pour postgres.js, mettez plutôt prepare: false dans les options du client. Le mode transaction ne prend pas en charge les requêtes préparées.
Quelle taille de pool pour chaque mode ?
Mode session : une ou deux par instance, avec un délai d’inactivité court, car le plafond est de quinze. Mode transaction : un pool normal, de cinq à dix, car les connexions sont rendues après chaque transaction.

Faites vos propres calculs avec le calculateur ou louez un runner.