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
  • firewalld active
  • A PostgreSQL role and database you want to pool (this tutorial uses appuser and appdb)

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.