PgBouncer for Production Applications

April 30, 2026

🚨 The Problem

While working on our production system, I noticed something concerning in PostgreSQL:

SELECT count(*) FROM pg_stat_activity;

Result: 1176 connections (active - 17, idle - rest)

This created:

Headache right?

💬 Wonder why this happens?

In microservices/Kubernetes environments:

And this leads to high connection count without actual load.

💡 The Solution: PgBouncer

PgBouncer is a lightweight connection pooler for PostgreSQL.

Instead of: App Pods → PostgreSQL

We use: App Pods → PgBouncer → PostgreSQL

Deployment Options

Option 1: Deploy it as a sidecar container on the production workload — but it's pointless to run as many containers as your production application pods.

Option 2 (chosen): Deploy it as a shared PgBouncer pool (centralized deployment). Deploy once and use it across multiple projects.

PgBouncer Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pgbouncer-pool
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: pgbouncer
          image: edoburu/pgbouncer:v1.25.1
          ports:
            - containerPort: 6432

Service

apiVersion: v1
kind: Service
metadata:
  name: pgbouncer
spec:
  ports:
    - port: 6432

PgBouncer Configuration

Add this as a Kubernetes Secret and attach it to the PgBouncer deployment:

[databases]
<db-name> = host=<db-host> port=5432 pool_size=25 user=<user> password=<password>

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

; Connection pool configuration
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3
admin_users = postgres

; Server connection settings
server_lifetime = 3600
server_idle_timeout = 600
server_connect_timeout = 15

; Performance tuning
max_db_connections = 50
max_user_connections = 50
ignore_startup_parameters = extra_float_digits

; Logging
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1

🔑 Updated Connection String

Your application database connection string would now point to PgBouncer:

postgres://user:pass@pgbouncer:6432/<db-name>

🔭 Observability

To understand PgBouncer behavior, use:

SHOW POOLS;
Column Meaning
cl_active Active client connections
sv_active Active DB connections
sv_idle Idle DB connections

Key insight: Client connections >> Server connections — which means pooling is working.

📉 Before vs After

Before PgBouncer After PgBouncer
Connections ~1176 (mostly idle) ~10–20 active
DB pressure High Minimal
System stability At risk Stable

🔭 Further Monitoring


If you're working on similar scaling problems or DevOps challenges, feel free to reach out or share your thoughts.