Troubleshooting PostgreSQL Connection Limit Reached: Database Pool Full on Ubuntu 22.04 LTS
Resolve 'PostgreSQL connection limit reached' errors on Ubuntu 22.04 LTS. Diagnose, adjust max_connections, optimize pooling, and prevent database service interruptions.
Resolve 'PostgreSQL connection limit reached' errors on Ubuntu 22.04 LTS. Diagnose, adjust max_connections, optimize pooling, and prevent database service interruptions.
This guide addresses a common yet critical issue in PostgreSQL environments: hitting the configured maximum connection limit. When your database connection pool is full, applications can no longer establish new connections, leading to service degradation or complete downtime. Users will typically encounter "connection refused" errors or internal server errors (HTTP 5xx) on web applications, signaling that the backend database is unreachable or overwhelmed.
Symptom & Error Signature
When the PostgreSQL connection limit is reached, applications will fail to connect, and the database server will log specific error messages.
Typical Application Log Errors:
For a Ruby on Rails application using pg gem:
PG::ConnectionBad: FATAL: remaining connection slots are reserved for non-replication superuser connections
from /var/www/app/shared/bundle/ruby/2.7.0/gems/activerecord-6.1.7/lib/active_record/connection_adapters/postgresql_adapter.rb:84:in `initialize'
...
For a Python Django application:
django.db.utils.OperationalError: FATAL: too many connections
For a Node.js application using pg module:
Error: FATAL: too many connections
at Connection.parseMessage (/app/node_modules/pg/lib/connection.js:639:13)
at Connection.handleReadyForQuery (/app/node_modules/pg/lib/connection.js:239:10)
...
PostgreSQL Server Log Errors (typically in /var/log/postgresql/postgresql-14-main.log or similar):
2023-10-26 10:30:05 UTC [12345]: [6-1] user=app_user,db=app_db,host=127.0.0.1 FATAL: too many connections
2023-10-26 10:30:05 UTC [12345]: [6-2] DETAIL: Sorry, too many clients already.
Or, if all slots are reserved for superusers (e.g., during a maintenance window or extreme load):
2023-10-26 10:30:05 UTC [12345]: [6-1] user=app_user,db=app_db,host=127.0.0.1 FATAL: remaining connection slots are reserved for non-replication superuser connections
Root Cause Analysis
The "PostgreSQL connection limit reached" error fundamentally means that PostgreSQL cannot accept new client connections because it has hit the max_connections limit defined in its configuration. Several factors can contribute to this state:
max_connectionsParameter Too Low: The most straightforward cause. The configuredmax_connectionsvalue inpostgresql.confis simply lower than the peak demand for connections from your applications or services.- Application Connection Leaks: Applications failing to properly close database connections after use can lead to a gradual accumulation of idle but active connections, eventually exhausting the pool.
- Inefficient Application Connection Pooling:
- No Pooling: Applications opening and closing connections for every request are highly inefficient and prone to hitting limits under load.
- Misconfigured Client-Side Pooling: Application-level connection pools (e.g., in ORMs like ActiveRecord, SQLAlchemy) might be misconfigured (e.g.,
pool_sizetoo high,timeouttoo short/long) or not effectively shared across application instances. - Lack of Server-Side Pooling (PgBouncer): For high-traffic applications, a dedicated connection pooler like PgBouncer is crucial. Without it, each application process/thread maintains its own direct connection to PostgreSQL, which quickly consumes
max_connections.
- Sudden Traffic Spikes: An unexpected surge in user traffic or background jobs can temporarily overwhelm the existing connection capacity if not adequately scaled.
- Long-Running or Blocking Queries: Queries that take an unusually long time to execute can hold connections open, preventing other processes from acquiring one. This can be exacerbated by locks, poor query plans, or lack of proper indexing.
- Excessive Idle Connections: Connections that are open but not actively performing any operations can still count against
max_connections. Whileidle in transactionconnections are problematic, evenidleconnections (no active transaction) consume a slot.
Step-by-Step Resolution
Addressing this issue typically involves a combination of diagnostics, configuration adjustments, and potentially application-level changes.
1. Diagnose Current Connection Usage
First, determine the current connection state and identify potential culprits.
Connect to your PostgreSQL database using psql as a superuser (e.g., postgres user):
sudo -u postgres psql
Then, run the following queries:
-- Count current active connections
SELECT count(*) FROM pg_stat_activity;
-- Show current max_connections setting
SHOW max_connections;
-- List all current connections, their state, and query
SELECT pid, datname, usename, client_addr, application_name, backend_start, state, state_change, query
FROM pg_stat_activity
ORDER BY backend_start DESC;
-- Identify connections that are 'idle in transaction' (often problematic)
SELECT pid, datname, usename, client_addr, application_name, backend_start, state, state_change, query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY backend_start DESC;
-- Find long-running queries
SELECT pid, datname, usename, application_name, client_addr, backend_start, xact_start, state,
current_timestamp - xact_start AS transaction_duration,
current_timestamp - query_start AS query_duration,
query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY query_duration DESC;
Use
htoportopon the database server to observe the number ofpostgresprocesses. Each active connection generally corresponds to apostgresprocess.
2. Increase max_connections (Temporary or as a First Step)
If max_connections is clearly too low for your immediate needs and you need a quick fix, you can increase it.
Increasing
max_connectionsdirectly increases the amount of RAM PostgreSQL will reserve, as each connection consumes memory (typically 2-4MB per connection forwork_memand other buffers). A value too high for your server's RAM can lead to swapping and severe performance degradation. Always monitor your server's memory usage after increasing this value.
Edit
postgresql.conf: The main configuration file is usually located at/etc/postgresql/14/main/postgresql.conffor PostgreSQL 14 on Ubuntu 22.04.sudo vim /etc/postgresql/14/main/postgresql.confLocate and modify
max_connections: Change the value to a higher number. A common starting point for a moderately busy server might be 100-200.# Connections max_connections = 200 # (or a suitable value based on your RAM and PgBouncer strategy)Restart PostgreSQL:
sudo systemctl restart postgresql@14-mainVerify: Connect with
psqlagain andSHOW max_connections;to confirm the change.
3. Implement or Optimize PgBouncer for Server-Side Connection Pooling
For robust, high-performance web applications, a dedicated connection pooler like PgBouncer is almost always a necessity. It acts as a proxy, maintaining a smaller set of connections to PostgreSQL while handling a much larger number of client connections.
Install PgBouncer:
sudo apt update sudo apt install pgbouncerConfigure PgBouncer: Edit the main configuration file, typically
/etc/pgbouncer/pgbouncer.ini.sudo vim /etc/pgbouncer/pgbouncer.iniHere's a sample configuration for a common use case:
[databases] # Format: <name> = host=<> port=<> dbname=<> user=<> password=<> app_db = host=127.0.0.1 port=5432 dbname=app_db user=pgbouncer_user password=YOUR_PGBOUNCER_PASSWORD [pgbouncer] listen_addr = 0.0.0.0 listen_port = 6432 auth_type = md5 auth_file = /etc/pgbouncer/userlist.txt # Client connections max_client_conn = 1000 ; Max client connections to PgBouncer default_pool_size = 20 ; How many server connections to allow per user/db pair pool_mode = session ; transaction or session (session is safer, transaction is more efficient but requires careful app design) reserve_pool_size = 5 ; Extra connections to reserve for emergencies reserve_pool_timeout = 5.0 ; seconds until a connection in reserve_pool is given out # Server connections (to PostgreSQL) max_db_connections = 0 ; Max connections per database. 0 means infinite. ; Best practice: Let default_pool_size manage it. max_user_connections = 0 ; Max connections per user. 0 means infinite. # Timeouts server_lifetime = 3600 ; If a server connection has been idle for this long, it's closed (seconds) server_reset_query = DISCARD ALL; SQL to run when connection is returned to pool server_check_query = SELECT 1 ; Health check queryCreate PgBouncer User (Authentication): PgBouncer needs a
userlist.txtfor authentication. You'll also need a dedicated PostgreSQL user that PgBouncer will use to connect to your database.First, create a user in PostgreSQL for PgBouncer to connect with:
sudo -u postgres psql -c "CREATE USER pgbouncer_user WITH PASSWORD 'YOUR_PGBOUNCER_PASSWORD';" sudo -u postgres psql -c "GRANT CONNECT ON DATABASE app_db TO pgbouncer_user;"Next, edit
/etc/pgbouncer/userlist.txt:sudo vim /etc/pgbouncer/userlist.txtAdd your application users and the
pgbouncer_user. For thepgbouncer_user, you'll use its plain text password or its MD5 hash (recommended for security). To get the MD5 hash:echo -n "YOUR_PGBOUNCER_PASSWORD" | md5sum(Note: This is a client-side MD5, not the PostgreSQL scram-sha-256 password hash. For
auth_type = md5inpgbouncer.ini, theuserlist.txtrequires the MD5 hash ofusername+password). For example:"pgbouncer_user" "md5c9944f2d5a3c631b34e55e0946258d4d"(replace with your actual hash)"app_user" "plain_app_user_password"(ifauth_typeallows plain, or use MD5 hash for app_user as well)For
auth_type = md5in PgBouncer, theuserlist.txtshould contain the username and the MD5 hash of the password as it would be stored inpg_authidif the user's password wasMD5(password+username). If your application users connect directly to PgBouncer, their credentials need to match what PgBouncer expects. A simplerauth_typefor initial setup might beanyortrustbut these are insecure.hba_fileis the most secure, delegating auth to PostgreSQL'spg_hba.conf. Forauth_type = md5, theuserlist.txtrequires the MD5 hash ofpasswordconcatenated withusername. Example calculation:md5(password + username). Or forauth_type = plain, just"username" "password". Thepgbouncer_userin the[databases]section is what PgBouncer uses to connect to PostgreSQL. The users listed inuserlist.txtare the users that connect to PgBouncer.Start/Restart PgBouncer:
sudo systemctl restart pgbouncer sudo systemctl enable pgbouncerConfigure Applications to use PgBouncer: Update your application's database connection string to point to PgBouncer's listening address and port (e.g.,
localhost:6432).For example, in a Rails
database.yml:production: adapter: postgresql encoding: unicode pool: 5 # Application-level pool, usually set lower when using PgBouncer host: localhost port: 6432 # Point to PgBouncer database: app_db username: app_user password: <%= ENV['DATABASE_PASSWORD'] %>When using PgBouncer, ensure your application's connection pool size (
poolin Rails) is set to a reasonable, smaller number (e.g., 5-10). The primary pooling is now handled by PgBouncer.
4. Optimize Application Code and Query Performance
This is often the most impactful long-term solution.
- Ensure Proper Connection Closure: Verify that your application framework or ORM properly closes connections, especially in asynchronous or multithreaded environments.
- Review Transaction Management:
- Minimize the duration of transactions.
- Avoid
idle in transactionstates by ensuring allBEGIN;statements are paired withCOMMIT;orROLLBACK;. - Use
SET LOCAL statement_timeout = '30s';to automatically cancel long-running queries within a session.
- Optimize Queries:
- Indexing: Ensure proper indexes are in place for frequently queried columns. Use
EXPLAIN ANALYZEto check query plans. - Avoid N+1 Queries: Especially common in ORMs.
- Batch Operations: Group multiple small operations into larger batches where possible.
- Indexing: Ensure proper indexes are in place for frequently queried columns. Use
- Refactor Bottlenecks: Identify and refactor parts of your application that are connection-heavy.
5. Monitor and Alert
Proactive monitoring is key to preventing future occurrences.
- PostgreSQL Metrics:
pg_stat_activity: Continuously monitor connection states.pg_stat_database: Track active queries, transaction counts.- Use tools like Prometheus/Grafana or Datadog to visualize
pg_stat_activitymetrics over time.
- PgBouncer Metrics:
- PgBouncer has its own admin console. Connect using
psql -p 6432 -U pgbouncer pgbouncer(thepgbouncerdatabase and user are special). SHOW STATS;SHOW POOLS;
- PgBouncer has its own admin console. Connect using
- System Metrics: Monitor CPU, RAM, I/O, and network usage on both your application and database servers. High I/O or CPU on the DB server can indicate slow queries, leading to connections backing up.
- Alerting: Set up alerts for high connection counts (e.g., when connections reach 80% of
max_connectionsormax_client_conn), long-running queries, and application error rates.
By systematically diagnosing, configuring, and optimizing, you can effectively manage PostgreSQL connections and ensure the stability and performance of your applications.
Our Production Verification Guarantee
Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.