'Too many connections': database connection pooling explained

'Too many connections' means every connection slot allowed by max_connections is taken, so the database refuses new clients. Fix it by reusing connections with a pool and closing leaks, not by raising the limit first.

Updated 6 min read

Learning objectives

After reading this article you will be able to:

  • Read the MySQL and PostgreSQL error messages and what limit they point to
  • Find out who is holding your connections
  • Set up a connection pool with a safe size for your app
On this page

Your app worked yesterday. Today it crashes on startup, or fails randomly under load, with a message that says the database has no room for you. The database is not down. It simply has a fixed number of connection slots, and something has used all of them.

This is one of the most common production errors for bots, APIs and websites, and it is almost always a client-side problem: too many processes, no pooling, or connections that never get closed.

Process 1Process 2Process 3newconnectionmax_connections3 × pool of 4 = 12 in useheadroomDatabaseToo many connections
Processes times pool size gives the total connections; it has to fit below max_connections, with headroom.

What does the error look like?#

MySQL (and MariaDB) report error 1040:

text
ERROR 1040 (HY000): Too many connections

In Node.js with mysql2, you see the same text with the code ER_CON_COUNT_ERROR.

PostgreSQL has two variants, both with SQLSTATE 53300:

text
FATAL:  sorry, too many clients already
FATAL:  remaining connection slots are reserved for roles with the SUPERUSER attribute

The first means the server reached max_connections. The second means only the slots held back for superusers are left: normal roles are locked out, but an administrator can still connect. Before PostgreSQL 16, the second message read "remaining connection slots are reserved for non-replication superuser connections".

What is a database connection, and why is it limited?#

A connection is a live session between a client and the server. In PostgreSQL each one is a separate server process; in MySQL each one gets a thread. Either way it holds server memory and internal state, even when it sits idle.

So the server caps the count with max_connections:

  • PostgreSQL: the default is typically 100, and superuser_reserved_connections holds 3 of them back. Changing max_connections requires a server restart.
  • MySQL: the default is 151. The server accepts max_connections + 1 clients, and the extra slot is for accounts with the CONNECTION_ADMIN privilege so an admin can still connect and inspect the problem.

Raising that number is not free, because every extra slot takes server resources.

Why do you run out of connections?#

From most to least common:

  1. No pool, one connection per request. Code that opens a new client on every request or event multiplies connections by traffic.
  2. Leaked connections. Your code checks out a client from the pool and never releases it, often because an error path skips release() or close().
  3. Too many processes. Each process has its own pool. Ten instances with a pool of 10 is 100 connections, the entire PostgreSQL default.
  4. Serverless or auto-restarting apps. Every cold start or crash loop opens fresh connections before the old ones time out on the server.
  5. Long transactions and slow queries. Connections stay busy, so the pool needs more of them.
  6. Other clients. Admin tools, migration jobs, cron scripts and dashboards also count against the same limit.

How do you see who is using the connections?#

On PostgreSQL, as a role allowed to see other sessions:

sql
SHOW max_connections;

SELECT count(*) AS total FROM pg_stat_activity;

SELECT usename, application_name, state, count(*)
FROM pg_stat_activity
GROUP BY usename, application_name, state
ORDER BY count(*) DESC;

Many rows in state idle with the same application_name are the classic sign of a leak or an oversized pool.

On MySQL:

sql
SHOW VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SHOW FULL PROCESSLIST;

Max_used_connections is the peak since the last restart. If it equals max_connections, you have hit the ceiling at least once. Sleep entries in the process list are idle connections.

How do you fix it with connection pooling?#

A pool opens a small number of connections once and lends them to your code. Queries wait briefly for a free one instead of opening a new one. Create the pool once at startup, share it across the whole process, and never create it inside a request handler.

With pg (node-postgres), whose pool defaults to a max of 10:

js
// db.js
import pg from "pg";

export const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 5, // upper bound for THIS process
  idleTimeoutMillis: 10_000, // close idle clients after 10 s
  connectionTimeoutMillis: 5_000, // fail fast instead of waiting forever
});

// For one-off queries, let the pool handle acquire/release:
const { rows } = await pool.query("SELECT now()");
console.log(rows[0]);

// For transactions, hold one client and ALWAYS release it:
const client = await pool.connect();
try {
  await client.query("BEGIN");
  await client.query("UPDATE accounts SET balance = balance - 10 WHERE id = $1", [1]);
  await client.query("COMMIT");
} catch (err) {
  await client.query("ROLLBACK");
  throw err;
} finally {
  client.release();
}

Do not run a transaction through pool.query, because each call may land on a different client.

With mysql2:

js
import mysql from "mysql2/promise";

export const pool = mysql.createPool({
  uri: process.env.DATABASE_URL,
  connectionLimit: 5,
  waitForConnections: true,
});

const [rows] = await pool.query("SELECT NOW() AS now");
console.log(rows[0]);

Python works the same way: create one engine (SQLAlchemy) or pool at import time and reuse it.

How big should the pool be?#

Do the multiplication before you pick a number:

text
total connections = processes x pool size

If you run 4 processes with a pool of 5, you use 20 connections. Add headroom for migrations, a SQL client you open by hand, and cron jobs, and keep the total well under the limit. A pool of 5 to 10 per process is usually enough, because a connection is only busy while a query runs, so one connection can serve many requests per second.

If a single process truly needs hundreds of simultaneous queries, the database is probably the bottleneck, and more connections will not make it faster.

When should you raise the limit or add PgBouncer?#

Raise max_connections when your pools are already sized sensibly and you simply have more processes than slots. On MySQL you can change it live:

sql
SET GLOBAL max_connections = 300;   -- lost on restart
SET PERSIST max_connections = 300;  -- survives restart

On PostgreSQL, edit postgresql.conf and restart. Remember that more connections use more memory.

When you have many processes or short-lived clients, put a pooler such as PgBouncer in front of PostgreSQL. In transaction pooling mode a server connection is assigned only for the duration of a transaction, so a small set of server connections can serve many more clients. The trade-off: features that depend on session state, such as SET, LISTEN, session-level advisory locks and SQL-level PREPARE, do not work reliably in that mode.

How do you keep it from coming back?#

  • Create one pool per process, at startup.
  • Release in finally blocks, and close the pool on shutdown with pool.end().
  • Set idle and acquire timeouts so problems fail loudly.
  • Give each app its own database user or application_name so you can tell who is connected.
  • Alert when Threads_connected or the row count of pg_stat_activity approaches 80 percent of the limit.

Not sure which engine fits your project? How to choose a database compares the options, what PostgreSQL is covers the engine itself, and how to host a database walks through setting one up.

How to do this on Square Cloud#

On Square Cloud you create the database in the dashboard and deploy your apps yourself, so pool settings live in your code and environment variables. Database hosting for MongoDB, PostgreSQL, MySQL and Redis starts on the Standard plan. Databases run in the same datacenter as your applications, with mandatory TLS.

Each database accepts up to 512 connections and there is no pooler in front, so your application's pool is what controls the connection count. Store the connection URL in an environment variable on each application and size each process's pool as shown above. Add up the pools of every application that points at the same database and keep the total well below that ceiling.

Every restart opens the pool again. Automatic restart is optional (it comes on when you deploy from the dashboard) and only acts when the process exits with an error. If the app falls into a crash loop, each round opens a new pool, so find the cause of the error in the logs and fix it.

For the engines and plans, see the database hosting page.

Database hosting

MongoDB, PostgreSQL, MySQL and Redis next to your app

Learn more

FAQs

What is the default max_connections in PostgreSQL and MySQL?

PostgreSQL defaults to 100 (it can be lower if the kernel does not support it) and reserves 3 slots for superusers. MySQL defaults to 151, plus one extra slot for administrator accounts.

Should I just raise max_connections?

Only after you check for leaks and add a pool. Every connection costs memory on the server, and PostgreSQL needs a restart to change the value. A higher limit often just delays the same error.

How many connections should a pool open?

Start small, around 5 to 10 per app process, and multiply by the number of processes and instances. The total must stay below the database limit with room left for admin tools and migrations.

What is the difference between a pool in my app and PgBouncer?

An in-app pool shares connections inside one process. PgBouncer sits in front of PostgreSQL and shares a smaller set of server connections across many processes or servers.

Next articleDatabase migrations on deployRun database migrations on deploy with Prisma, Drizzle or Alembic: one step before the new code starts, safe against failures. Commands, checks and mistakes.

More in Databases

See all 24 articles