Too many connections / remaining connection slots are reserved

Fix "sorry, too many clients already" and "remaining connection slots are reserved" on SupaHost, where 400 connections are shared by every project on the instance.

What you see

When a client tries to open a new connection:

FATAL:  53300: sorry, too many clients already

or

FATAL:  53300: remaining connection slots are reserved for roles with the SUPERUSER attribute

On SupaHost you'd normally see these from your own tools connected through the SSH tunnel (psql, an ORM, a migration runner). Through the Data API, a shortage of connections shows up as slow requests or PGRST003 errors instead.

Why it happens

Postgres has a fixed number of connection slots (max_connections), and a few are held back for administrators. When the rest are in use, new connections are refused. Each connection is a separate server process using memory, so the limit protects the VM.

On SupaHost, one Postgres server serves every project on the instance, with max_connections = 400 shared between them. Each project's services use small fixed pools (5 connections for the Data API and 5 for Auth), plus a few for Storage, Realtime and Studio while it's open. Those rarely add up to 400. The usual culprit is client-side pooling:

  • An ORM pool of 10 to 20 connections in each app process, multiplied by several processes or workers.
  • Dev servers with hot reload that create a new pool on every reload without closing the old one.
  • Scripts that open a connection per task and never close it.
  • Sessions left idle in transaction by a crashed script or an open psql prompt.

How to fix it

  1. See who holds the connections, across all projects:

    select datname, usename, application_name, state, count(*)
    from pg_stat_activity
    group by datname, usename, application_name, state
    order by count(*) desc;
    

    datname is the project database (p_<project>). Many rows for postgres with state idle usually means a client pool.

  2. Close idle connections that your own tools left open. The postgres role can end sessions of its own role:

    select pg_terminate_backend(pid)
    from pg_stat_activity
    where usename = 'postgres'
      and datname = current_database()
      and state in ('idle', 'idle in transaction')
      and state_change < now() - interval '10 minutes'
      and pid <> pg_backend_pid();
    

    Studio also connects as postgres; if you close its connections, it reconnects on the next query.

  3. Cap your client pools. Five connections per process is plenty for most apps:

    import pg from 'pg'
    
    const pool = new pg.Pool({
      connectionString: process.env.DATABASE_URL,
      max: 5,
      idleTimeoutMillis: 30_000,
    })
    

    For Prisma, add connection_limit=5 to the connection string.

  4. Create one pool per process and reuse it. In dev servers, keep the pool on a global so hot reloads don't create new ones.

  5. Have abandoned transactions closed automatically, for example in the connection string: ?options=-c%20idle_in_transaction_session_timeout%3D60000 (60 seconds).

On SupaHost

  • There's no connection pooler (Supavisor or PgBouncer) on SupaHost, so every client connection is a real Postgres connection. The Data API pools for you, which is why browser and mobile apps should use supabase-js rather than connect to Postgres.
  • Direct connections are only possible on Business and Enterprise, through the SSH tunnel. Serverless functions that open a fresh connection per request are a poor fit; use the Data API or an Edge Function instead.
  • max_connections is the same on every plan, so upgrading doesn't add slots. It does add memory, which helps if you're running many connections at once.

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us