← blog

Supabase 'max clients reached in session mode' na Vercel, e a correção que piorou tudo

20 de setembro de 2026 · 7 min de leitura

O erro é (EMAXCONNSESSION) max clients reached in session mode - max clients are limited to pool_size: 15. Nossa API começou a devolver esse erro enquanto o site de marketing continuava perfeito, porque o site de marketing não usa o Postgres. É exatamente por isso que esse tipo de falha passa despercebido. As partes que parecem bem são as que não precisam do banco.

Veja o que o erro significa, os dois erros que cometemos ao corrigir e a string de conexão que funciona de verdade a partir da Vercel.

Dois poolers, duas portas

Por que o serverless esgota o session mode

Cada instância aquecida de função tem o próprio pool pequeno. O nosso tinha três. Um pool em session mode nunca devolve as conexões enquanto a instância está aquecida. Cinco instâncias aquecidas são quinze conexões, que é o limite. A sexta requisição, venha de onde vier, falha.

Um daemon de frota consultando três endpoints a cada poucos segundos mantinha instâncias aquecidas o tempo todo. Nada disso é incomum. É só uma conta que o session mode perde.

Erro um: corrigir a camada errada primeiro

A correção de emergência foi max: 1 por instância e um idle timeout de vinte segundos, ainda no session mode. Funcionou. Instâncias que ficavam quietas devolviam a vaga, e a API se recuperou em minutos. Depois passamos a string de conexão para o transaction mode, o que era o certo. Deixamos o max: 1 no lugar, o que não era.

Erro dois: um pool de uma conexão no transaction mode

No transaction mode, uma única conexão por instância coloca todas as queries daquela instância em fila atrás de um só cano. As consultas do daemon ficavam na fila, as requisições passavam do prazo de vinte segundos, e o sintoma mudou de 500 para travamentos. O site de marketing continuava rápido. A API parou de responder de vez.

O transaction mode devolve a conexão depois de cada transação, então ele pede um pool de tamanho normal. Colocamos oito e os timeouts pararam.

Como ver o problema chegando

O pooler não avisa nada até recusar você. O próprio Postgres avisa, se você perguntar.

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

Rode isso em uma conexão de sessão com o app sob carga normal. Se a contagem estiver subindo em direção ao limite e a maioria das linhas estiver idle, você está a um minuto movimentado do erro. No transaction mode, a mesma query mostra as conexões sendo recicladas em vez de acumuladas.

A string de conexão que funciona de verdade

postgresql://postgres.<project-ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres
  • O host é o pooler, não db.<ref>.supabase.co. O painel do Supabase pode mostrar o host direto sob um título de transaction pooler. Continua sendo o host direto.
  • O usuário é postgres.<project-ref> no pooler. O postgres puro só funciona no host direto.
  • O host direto é só IPv6, a menos que você compre o add-on de IPv4. Ele tem um registro AAAA e nenhum registro A. As funções da Vercel não chegam nele, e a porta 6543 nem está aberta lá.
  • Não precisa de query string no postgres.js. Use prepare: false. O parâmetro ?pgbouncer=true é uma convenção do Prisma.
  • Mantenha as migrations na 5432. O Drizzle e ferramentas parecidas querem uma conexão de sessão. Aponte o app publicado para a 6543 e o ambiente local de migrations para a 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

Tamanhos, em uma tabela

Uma última coisa que vale saber. Enquanto tudo isso acontecia, o daemon que não conseguia alcançar a API concluiu que todas as máquinas que ele gerenciava estavam mortas. Ele se preparou para cortar a energia delas. Essa história está em como um limite de conexões do banco de dados quase reiniciou os Macs dos meus clientes.

Perguntas

Qual porta do Supabase um app na Vercel deve usar?
6543, o pooler em transaction mode. O session mode na 5432 segura uma conexão de servidor por cliente e limita o projeto a 15, o que o serverless esgota rápido.
Por que db.<project-ref>.supabase.co não conecta a partir da Vercel?
O host direto só tem endereço IPv6, a menos que você pague o add-on de IPv4, e as funções da Vercel não chegam nele por IPv6. Use o host do pooler, aws-0-<region>.pooler.supabase.com.
Qual usuário o pooler precisa?
postgres.<project-ref>, com o ref do projeto no final. O postgres puro só funciona no host direto.
Preciso de ?pgbouncer=true na string de conexão?
Para Prisma, sim. Para postgres.js, coloque prepare: false nas opções do cliente; o transaction mode não aceita prepared statements.
Que tamanho de pool usar em cada modo?
Session mode: uma ou duas por instância, com um idle timeout curto, porque o limite é quinze. Transaction mode: um pool normal, de cinco a dez, porque as conexões são devolvidas depois de cada transação.

Faça suas próprias contas na calculadora ou alugue um runner.