2026.09 / connection capacity 064
Coolify PostgreSQL too many connections? Fix it safely
A Coolify application can suddenly return database errors while PostgreSQL logs FATAL: sorry, too many clients already or remaining connection slots are reserved. Restarting the database may briefly clear the symptom, but it also interrupts every client and hides the process that consumed the slots.
The useful question is not simply “How do I increase max_connections?” It is “Which application, process type, deployment, or transaction owns the sessions, and why are they not being reused?” Measure that first. Then recover only the capacity you need, correct the application pool budget, and verify database-backed work.
Do not delete the PostgreSQL volume, expose port 5432 publicly, or terminate every backend as a first response. A connection-capacity incident is not a storage-reset problem.
What the two common errors mean
| Error | Meaning | First focus |
|---|---|---|
too many clients already | The server has no connection slot available to this client. | Session ownership and pool totals. |
remaining connection slots are reserved | Ordinary clients filled the non-reserved allowance. | Use an authorised administrative route; preserve reserved capacity. |
connection refused | No server accepted the TCP connection at that address and port. | Host, port, network, readiness, and listen address. |
password authentication failed | PostgreSQL was reached but rejected the credential. | Role, password, and connection URL. |
If the error is refusal or authentication rather than exhaustion, use the separate guides for Coolify PostgreSQL connectivity and password drift. Opening a public port or rotating a password cannot create a free backend slot.
1. Preserve one administrative path
PostgreSQL keeps a small number of slots away from ordinary roles so an administrator can still investigate. Do not point application traffic at a superuser to bypass the limit. That defeats the safety margin and gives the application unnecessary privileges.
Use Coolify’s database terminal or an authorised psql session from the private application network. If no administrative session can connect, stop or scale down the single known noisy application process before restarting the database. That is narrower than rebooting every client. Keep the database on its Coolify private network and use the generated Internal URL rather than publishing PostgreSQL to the internet.
2. Capture the limit and current usage
Once connected, record the capacity settings and the number of visible sessions. Current PostgreSQL versions can have ordinary, reserved, and superuser-reserved capacity; the exact settings available depend on the running major version.
SHOW max_connections;
SHOW superuser_reserved_connections;
SELECT count(*) AS visible_sessions
FROM pg_stat_activity;
Do not assume the row count seen by a restricted role is the complete operational picture. Use an authorised monitoring or administrative role. Also leave deliberate headroom for migrations, backups, console access, health operations, and a real incident. A design that uses every ordinary slot during normal traffic is already over capacity.
3. Find who owns the sessions
Start with aggregate evidence, not a dump of complete queries. Group by database, role, application name, and state:
SELECT
datname,
usename,
application_name,
state,
count(*) AS sessions
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
GROUP BY datname, usename, application_name, state
ORDER BY sessions DESC, datname, usename;
Then inspect age and wait state for the suspicious group without copying sensitive SQL text into a public ticket:
SELECT
pid,
datname,
usename,
application_name,
state,
wait_event_type,
wait_event,
backend_start,
xact_start,
state_change
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
ORDER BY xact_start NULLS LAST, backend_start;
Match application_name, role, database, timestamps, and Coolify resources. If the application does not set an application name, add one to each connection configuration—for example separate names for web, worker, scheduler, and migration processes. Attribution becomes much easier during the next incident.
Idle is not the same as idle in transaction
active: the backend is executing a query. A large active set may reflect real load, slow SQL, lock waits, or excessive concurrency.idle: the client keeps a connection open while waiting for its next command. This is normal for a bounded pool, but hundreds of idle sessions can reveal oversized pools or excessive process replicas.idle in transaction: the client opened a transaction and stopped issuing commands. It still consumes a slot and can retain locks or an old snapshot. Find the code path and transaction boundary.
A count of idle sessions alone does not prove a leak. Ten application containers with a pool maximum of 20 can legitimately retain about 200 sessions even when request traffic is quiet. The aggregate deployment budget matters.
4. Recover only the capacity you need
The safest immediate action is usually to stop or reduce the offending client, allowing it to close its own sessions. For a bad deployment, roll back or scale down its web and worker replicas. If one runaway query is the issue, cancel that query before terminating its whole session.
When a backend must be disconnected, target verified process IDs and exclude your own session. pg_cancel_backend requests cancellation of the current query; pg_terminate_backend ends the session. Both require suitable privileges.
-- Review the selected rows first. Never paste an unreviewed mass-kill query.
SELECT pg_cancel_backend(PID_TO_CANCEL);
SELECT pg_terminate_backend(PID_TO_TERMINATE);
Terminating a session rolls back its open transaction and makes the client reconnect. That can duplicate work if a job retries without idempotency, and it can create a reconnect storm if the application pool is still misconfigured. Do not terminate autovacuum, replication, backup, or unfamiliar internal sessions merely to make the graph drop.
5. Calculate one connection budget for the whole deployment
Application pool settings are normally per process, not per Coolify application. Calculate the worst case across every process that can connect:
web replicas × web pool maximum
+ worker replicas × worker pool maximum
+ schedulers and listeners
+ migration and release jobs
+ monitoring and backup clients
+ administrative headroom
≤ usable PostgreSQL capacity
For example, changing a web pool from 10 to 30 does not add 20 sessions overall if four replicas exist; it can add 80. Development defaults copied into multiple web, worker, and scheduler containers are a common source of surprise.
Set the pool minimum low enough that quiet processes do not pre-open unnecessary sessions. Set the maximum from measured query concurrency and database capacity rather than web concurrency alone. Configure acquisition timeouts so requests fail in a controlled way instead of waiting forever. Confirm connections are returned in every success, error, cancellation, and timeout path.
After changing Coolify runtime variables, recreate the relevant application and worker containers. A saved variable does not resize a pool already running in memory. The Coolify environment-variable guide covers build-time values, runtime values, and hidden overrides.
6. Treat poolers and max_connections as design changes
PgBouncer can let many client-side sessions share fewer PostgreSQL backends, especially for short transactions. It is not a repair for leaked transactions or slow queries. Transaction pooling also changes session semantics: code relying on session-level state, temporary tables, advisory locks, or some prepared-statement behaviours needs compatibility testing.
Do not blindly raise max_connections. PostgreSQL allocates per-backend resources, while concurrent queries can multiply memory use through settings such as work_mem. The parameter is server-start only, so changing it requires a planned restart. First right-size application pools and remove leaks; then raise the limit only after checking memory, CPU, workload, operational headroom, and rollback.
Coolify can apply resource limits to the database container, but a higher PostgreSQL limit does not create more container memory. Container limits, PostgreSQL configuration, and aggregate application pools must agree.
7. Use timeouts as guardrails, not substitutes for correct code
idle_in_transaction_session_timeout can end sessions that remain idle while holding an open transaction. It protects against one damaging failure mode, but the client must still handle disconnection and retry safely. statement_timeout limits query runtime, not session count. Generic idle-session timeouts can fight a legitimate pool and cause reconnect churn.
Introduce timeouts from observed transaction and query durations, preferably per role or application where possible. Test long migrations, maintenance, imports, backups, and workers before making a broad database-wide change.
8. Verify the fix through the real application
- Confirm the deployment or worker change reached a terminal successful state.
- Run a database-backed read through the application—not only a static health endpoint.
- Perform one controlled write and read-back if the environment permits it.
- Watch
pg_stat_activitythrough normal traffic and one application restart. - Confirm session counts settle below the planned budget rather than climbing continuously.
- Check for old
idle in transactionsessions, lock waits, and reconnect loops. - Run a scheduled job and verify worker progress.
- Confirm a fresh backup completes; do not consume the reserved operating margin with the backup job.
An HTTP 200 from a route that never queries PostgreSQL proves only that the web process answered. A reliable check exercises the data path that failed and then confirms connection behaviour remains bounded.
Compact incident order
- Match the application error to the PostgreSQL connection-limit error.
- Preserve an authorised administrative path and do not grant the app superuser access.
- Record limits and group
pg_stat_activityby owner, process name, database, and state. - Distinguish active, idle, and idle-in-transaction sessions.
- Stop or reduce the offending client before terminating selected backends.
- Calculate pool maximums across every replica, worker, scheduler, and migration job.
- Recreate affected Coolify resources so new runtime limits take effect.
- Consider PgBouncer, timeouts, or a higher server limit only after workload testing.
- Verify real reads, writes, jobs, backups, and a stable session count.
The durable fix is usually outside the database restart button. PostgreSQL enforces one finite server budget while Coolify may run several independent processes, each with its own pool. Make those numbers explicit, retain operational headroom, and use session evidence to find leaks or oversized pools. Then a traffic spike or routine redeploy is far less likely to consume the last ordinary slot.
Authoritative references: Coolify’s official PostgreSQL guide describes its standalone container, private Internal URL, persistent storage, health checks, and resource controls. PostgreSQL documents connection settings and reserved slots, the fields in pg_stat_activity, query cancellation and session termination, and client-session timeouts. The PgBouncer feature matrix explains pooling modes and their compatibility trade-offs.