PgBouncer is a lightweight connection pooler for PostgreSQL that sits between your application and the database server, reusing a small pool of backend connections across many client requests. Without a connection pooler, PostgreSQL must fork a new backend process for every client connection, consuming memory and CPU as connection counts grow. On RHEL 8, PgBouncer is available from the EPEL 8 repository and is straightforward to configure. In this tutorial you will install PgBouncer, configure transaction-mode pooling, set up MD5 password authentication, and verify the pool through the PgBouncer admin console.
Prerequisites
- RHEL 8 server with PostgreSQL 12 or later running (local or remote)
- EPEL 8 repository enabled
- Root or sudo access
firewalldactive- A PostgreSQL role and database you want to pool (this tutorial uses
appuserandappdb)
Step 1 — Enable EPEL 8 and Install PgBouncer
Install the EPEL repository and then PgBouncer:
sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
sudo dnf install -y pgbouncer
pgbouncer --version
Step 2 — Configure pgbouncer.ini
The main configuration file is /etc/pgbouncer/pgbouncer.ini. Open it for editing and replace the default content with a production-ready configuration:
sudo vi /etc/pgbouncer/pgbouncer.ini
[databases]
appdb = host=127.0.0.1 port=5432 dbname=appdb
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
; Transaction pooling is the most efficient mode for most web apps
pool_mode = transaction
; Maximum total client connections PgBouncer will accept
max_client_conn = 200
; Number of server connections per (database, user) pair
default_pool_size = 20
; Connections to keep open even when idle
min_pool_size = 5
; Reserve connections for superuser queries
reserve_pool_size = 5
reserve_pool_timeout = 3
; Close server connections that have been idle longer than this (seconds)
server_idle_timeout = 600
; Logging
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1
; Admin access
admin_users = pgbouncer_admin
stats_users = pgbouncer_stats
pool_mode = transaction is recommended for most OLTP workloads because connections are released back to the pool after every transaction, not after every session. Use session mode only if your application uses session-level features such as advisory locks or SET LOCAL.
Step 3 — Create the userlist.txt Authentication File
PgBouncer reads credentials from userlist.txt in quoted name and MD5-hashed password format. Generate the MD5 hash by concatenating the password and username, then MD5-hashing the result:
# Generate MD5 hash for appuser with password 'AppUserPass1!'
# The hash format is: md5 + md5(password + username)
echo -n 'AppUserPass1!appuser' | md5sum
Copy the resulting hash and add it to the userlist file. Also add the admin user:
sudo vi /etc/pgbouncer/userlist.txt
"appuser" "md5REPLACE_WITH_YOUR_HASH_HERE"
"pgbouncer_admin" "md5REPLACE_WITH_ADMIN_HASH_HERE"
sudo chown pgbouncer:pgbouncer /etc/pgbouncer/userlist.txt
sudo chmod 640 /etc/pgbouncer/userlist.txt
Ensure the matching PostgreSQL roles exist in the database:
sudo -u postgres psql -c "CREATE ROLE appuser WITH LOGIN PASSWORD 'AppUserPass1!';"
sudo -u postgres psql -c "GRANT CONNECT ON DATABASE appdb TO appuser;"
Step 4 — Enable and Start PgBouncer
sudo systemctl enable --now pgbouncer
sudo systemctl status pgbouncer
Check the PgBouncer log for any startup errors:
sudo journalctl -u pgbouncer -n 40 --no-pager
Step 5 — Connect Applications Through PgBouncer
Applications connect to PgBouncer on port 6432 using the same credentials as they would for PostgreSQL directly. The only change is the port number:
# Test connection through the pooler
psql -h 127.0.0.1 -p 6432 -U appuser -d appdb -c "SELECT current_database(), pg_backend_pid();"
Run the same command several times; you will see different backend PIDs returned, demonstrating that PgBouncer is reusing a small set of backend connections across multiple client sessions.
Step 6 — Monitor Pool Statistics
Connect to the PgBouncer admin console to inspect pool health:
psql -h 127.0.0.1 -p 6432 -U pgbouncer_admin -d pgbouncer
SHOW POOLS;
SHOW STATS;
SHOW CLIENTS;
SHOW SERVERS;
Key columns in SHOW POOLS: cl_active (clients actively using a server connection), cl_waiting (clients waiting for a free server connection), sv_active (server connections in use), and sv_idle (server connections available). If cl_waiting is consistently above zero, increase default_pool_size.
Open firewall port 6432 only to trusted application hosts:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="6432" protocol="tcp" accept'
sudo firewall-cmd --reload
Conclusion
You have installed PgBouncer from EPEL 8 on RHEL 8, configured transaction-mode connection pooling with MD5 authentication, created the userlist credentials file with correct permissions, started the service, and verified pool statistics through the admin console. Applications connecting through PgBouncer on port 6432 will now share a compact pool of backend PostgreSQL connections, dramatically reducing memory usage and improving throughput under high concurrency.
Next steps: Configuring PgBouncer with SCRAM-SHA-256 Authentication, Setting Up PgBouncer with TLS Encryption on RHEL 8, and Monitoring PgBouncer with Prometheus.