PostgreSQL Connection Pooling: PgBouncer Setup with Go and Node.js
If every app instance opens its own connections to PostgreSQL, you hit max_connections fast. Connection pooling — especially PgBouncer — multiplexes many client sessions onto fewer real DB connections.
This article covers PgBouncer deployment basics and practical Go and Node.js client settings.
What goes wrong?
- Each PostgreSQL connection has memory and process cost
- Serverless or high-replica Kubernetes clusters multiply connection counts
- Opening a new connection per request adds latency
Pattern: App → PgBouncer (pool) → PostgreSQL.
PgBouncer pool modes
- Session: one server connection for the whole client session (most compatible, least sharing)
- Transaction: return server connection after each transaction (most common for APIs)
- Statement: rare and restrictive
Most HTTP APIs use transaction pooling. Some session features (including certain prepared statement flows) need care — verify your ORM settings.
Docker sketch
services:
pgbouncer:
image: edoburu/pgbouncer:latest
environment:
DATABASE_URL: postgres://user:pass@db:5432/appdb
POOL_MODE: transaction
MAX_CLIENT_CONN: 1000
DEFAULT_POOL_SIZE: 25
ports:
- "6432:5432"
Production adds userlist, TLS, auth_query, and monitoring.
Go: database/sql
import "database/sql"
import _ "github.com/jackc/pgx/v5/stdlib"
db, err := sql.Open("pgx", os.Getenv("DATABASE_URL"))
if err != nil { log.Fatal(err) }
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(time.Hour)
Behind Pgouncer, keep application-side MaxOpenConns moderate — avoid double-stacking huge pools.
Node.js: pg
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 15,
idleTimeoutMillis: 30000,
});
If you see prepared-statement issues with transaction pooling, look for “disable prepared statements” options in your driver/ORM.
Observability
- PgBouncer:
SHOW POOLS,SHOW STATS - Postgres:
pg_stat_activity - App metrics: wait time on checkout, timeouts
Summary
- PgBouncer in transaction mode is the default answer for PostgreSQL connection pooling at scale.
- Tune Go and Node.js pools to be small and predictable behind the bouncer.
- Test ORM + PgBouncer together for prepared statement behavior.
See also gRPC vs REST when designing service-to-service database access patterns.
Related posts
PostgreSQL Query Performance: Indexes, Read/Write Trade-offs, and Advanced Concepts
From B-tree to partial and covering indexes; reading EXPLAIN ANALYZE; how indexes speed reads and what they cost on INSERT, UPDATE, DELETE, and VACUUM.
PostgreSQL Sharding with GORM: Deep Technical Guide
A detailed guide to PostgreSQL sharding architecture, shard-key strategy, query routing, cross-shard trade-offs, and production-ready implementation patterns with GORM.
PostgreSQL Partitioning: A Deep Technical Guide
A practical deep dive into PostgreSQL partitioning with range/list/hash strategies, partition pruning, index design, migration patterns, and operational best practices.