Mert Tosun
← Posts
PostgreSQL Connection Pooling: PgBouncer Setup with Go and Node.js

PostgreSQL Connection Pooling: PgBouncer Setup with Go and Node.js

Mert TosunBackend

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.