Resolving Heroku Postgres Connection Exhaustion and Idle Dyno Churn
A diagnostic guide for Heroku Postgres connection pool exhaustion and dyno sleep churn, with ordered checks, configuration fixes, and escalation criteria.
09 Sept 2025, 17:27 UTC

The Symptom: Connections Fail After Load or Idle Periods
Your Heroku application starts throwing database errors such as FATAL: remaining connection slots are reserved or too many clients. Requests hang, then time out. The pattern often appears after a traffic spike, a scale-up, or a quiet period when free dynos have slept. The useful takeaway: before upgrading your database plan, verify whether the problem is plan capacity, oversized application pools, long-running queries, or dyno sleep churn.
Cause and Diagnostic Matrix
| Observable Condition | Likely Cause | Primary Check |
|---|---|---|
remaining connection slots are reserved or too many clients | Database plan connection cap reached | heroku pg:info |
| Requests stall; sessions stay active for many seconds | Long-running queries holding pool slots | heroku pg:top or pg_stat_activity |
| Connection count jumps on boot or deploy | Application pool size exceeds available headroom | Review DATABASE_URL and ORM pool settings |
| Repeated connection resets after idle time | Free dyno sleep and wake churn | heroku logs --tail |
Ordered Checks
1. Compare Current Connections to the Plan Ceiling
Run the following from your local terminal using the Heroku CLI. You need read access to the target app.
heroku pg:info -a your-app-name
Look for the connections value and the max_connections value. If connections is at or near max_connections, the database has no headroom for new clients. This check is read-only and carries no risk.
2. Inspect Active Sessions for Long Queries
Long-running queries tie up connections that other dynos need. Use the Postgres top utility:
heroku pg:top -a your-app-name
Or query the live session view directly. pg_stat_activity is a Postgres system view that lists every active server process.
heroku pg:psql -a your-app-name -c "SELECT pid, state, query_start, query FROM pg_stat_activity WHERE state != 'idle';"
Look for rows where query_start is many seconds or minutes in the past. Those sessions are holding pool slots. This check is also read-only.
3. Audit Application-Side Pool Arithmetic
A connection pool is a set of reusable database connections maintained by your application or ORM (object-relational mapper). If every process opens more connections than the plan allows, exhaustion happens even under moderate load.
Example: three web dynos each configured with a pool size of 10, plus one worker dyno with a pool size of 5, can demand 35 simultaneous connections. If the plan ceiling is close to that number, a single restart or burst will exceed it.
Check whether DATABASE_URL contains a pool parameter, and review your ORM configuration (for example, SQLAlchemy pool_size or ActiveRecord pool). This audit is read-only.
4. Check for Dyno Sleep Churn
Free dynos sleep after roughly 30 minutes of inactivity. When traffic resumes, every dyno wakes and re-establishes its connection pool at once, creating a connection spike.
Tail the logs to confirm:
heroku logs --tail -a your-app-name
Look for idle or sleep messages followed by a burst of connection establishments or resets. This check is read-only.
Fixes Tied to Findings
Finding A: Plan Ceiling Reached
If the database is genuinely at its connection cap, reduce the number of direct clients by enabling connection pooling. Heroku Postgres supports connection pooling, and PgBouncer—a lightweight connection pooler for Postgres—can sit between your app and the database to multiplex many client connections onto fewer server connections.
Alternatively, upgrade the Postgres plan to raise max_connections. To roll back, disable pooling or downgrade to the previous plan.
Finding B: Long-Running Queries
Optimize the offending SQL. Add missing indexes with CREATE INDEX, refactor inefficient joins, or break large transactions into smaller units so connections are released sooner.
Index creation is a schema change. Test it on a follower database first when possible. Roll back with DROP INDEX index_name;.
Finding C: Pool Size Misconfiguration
Lower the application pool size so that total potential connections stay comfortably below max_connections. A common target is 80 percent of the ceiling, divided by the number of dynos and processes.
You can attempt to set the pool parameter on DATABASE_URL, though support depends on your language buildpack and database client:
heroku config:set DATABASE_URL='postgres://user:pass@host:5432/dbname?pool=5' -a your-app-name
Replace the placeholders with your actual credentials. If your client honors the parameter, pool=5 limits the process to five connections. Otherwise, set the pool size directly in your ORM configuration. Restart the app so the new value takes effect.
Roll back by restoring the previous DATABASE_URL value. Risk: a pool that is too small increases request queuing and latency.
Finding D: Dyno Sleep Churn
Upgrade the dyno to Hobby or a professional tier so it no longer sleeps. If upgrading is not an option, use Heroku Scheduler to send a lightweight request to the app every 10 to 20 minutes, keeping the dyno awake.
Roll back by downgrading the dyno or removing the Scheduler job.
Escalation Criteria
Open a support ticket when:
- Connections still exceed
max_connectionsafter you have lowered pool sizes and optimized slow queries. - Query latency remains above 10 seconds after adding indexes and refactoring SQL.
- Connection pooling is saturated and a plan upgrade is not feasible for your workload.
- Connection resets continue after you have eliminated dyno sleep as a variable.
Limitations and Verification
Heroku connection limits vary by plan and can change over time. Always verify the current ceiling for your specific plan in the official Heroku documentation rather than relying on remembered numbers. Connection pooling adds a small amount of latency because sessions are multiplexed. Pool size changes affect overall throughput, so monitor request latency after adjustment.
To verify the fix, run heroku pg:info -a your-app-name again and confirm that connections remains below max_connections with reasonable headroom. Then tail the logs through a full traffic cycle, including any idle-to-active transition, and confirm that connection-slot errors no longer appear.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.