This post is maintained by Bytebase, an open-source database governance platform. We update the post every year.
Postgres's connection model is the part that bites. Each client connection is a dedicated backend process with real memory and CPU cost, opening connections is expensive, and even idle sessions hold resources. Picking max_connections is guesswork: too low and your app queues, too high and the database burns RAM on connections that mostly sit idle.
A connection pooler sits between your apps and Postgres, reusing a small set of server connections across many clients.
Here's a tour of the leading open-source options.
PgBouncer
https://github.com/pgbouncer/pgbouncer
What it is. A lightweight, single-binary pooler that's been in production for years. It supports session, transaction, and statement pooling modes, per-user/per-database connection limits, and prepared statements in transaction mode. It's actively maintained — the 1.25.2 release (May 2026) fixed four CVEs in SCRAM and admin-console handling.
Why it's popular:
- Small footprint, deploys almost anywhere.
- Works with managed Postgres (RDS, AlloyDB, etc.) and on-prem clusters.
- Wired into many operators (CloudNativePG ships a
PoolerCRD).
The caveat: PgBouncer is single-threaded. A busy instance bottlenecks on one CPU core; the standard remedy is several instances behind a load balancer. (Cloudflare archived its multi-tenant fork in June 2026 — upstream is the way.)
Good fit for: most apps that need stable concurrency control and simple operations.
Odyssey
https://github.com/yandex/odyssey
What it is. A multi-threaded C pooler from Yandex, built because a single-threaded PgBouncer didn't survive Yandex-scale traffic. It does session and transaction pooling plus a richer request-routing layer, and it's proven in very large production setups — Yandex runs it in front of its managed Postgres fleet. v1.5.0 landed in January 2026.
Why it scales:
- Worker threads share the global connection pools, so one instance spreads across cores — no fleet of single-threaded processes each hoarding its own connections.
- Transaction pooling cleans up after clients: Odyssey tracks transaction state, and when a client vanishes mid-transaction it cancels the running query and rolls back the abandoned transaction before the connection is reused. Since v1.5.0, prepared statements survive transaction pooling by default (
pool_reserve_prepared_statement). - Operations at fleet scale: per-connection UUIDs for end-to-end log tracing, built-in Prometheus metrics, and auth that reaches beyond md5/SCRAM to PAM and LDAP, with full TLS on both sides.
The trade-off is complexity. Odyssey brings more knobs, its own configuration language, and documentation that's thinner than PgBouncer's — it assumes you have (or are) a platform team.
Good fit for: high-throughput workloads where PgBouncer saturates a core and you'd rather scale up one pooler than orchestrate a fleet.
PgDoorman
https://github.com/ozontech/pg_doorman
What it is. A multi-threaded Rust pooler from Ozon, one of the largest e-commerce platforms in Russia — in production there for over three years across Go, .NET, Python, and Node.js workloads. It does session and transaction pooling under an MIT license; v3.11.0 shipped in June 2026. digoal, the prolific Chinese Postgres evangelist, put it on the map in July 2026.
Why it stands out — transparent transaction pooling:
- Prepared statements without driver surgery: anonymous statements are remapped to named ones, so backends reuse plans.
- One shared pool across all worker threads — no multi-instance fleet with skewed load.
- Admin console, graceful binary upgrades, Prometheus exporter, Patroni-assisted failover.
The caveat: it's deliberately narrow — no load balancing, sharding, or routing — and the transparency covers the protocol, not session semantics: SET and LISTEN/NOTIFY still break. (Its self-reported benchmarks run 3–4× ahead of PgBouncer, disclaimed by the project itself: "Benchmarks always lie to you.")
Good fit for: teams whose blocker is transaction pooling's compatibility tax, and who want pooling only — nothing else.
PgDog
https://github.com/pgdogdev/pgdog
What it is. A multi-threaded Rust proxy that rolls connection pooling, load balancing, and sharding into one layer, backed by Y Combinator and $5.5M in funding. It succeeds PgCat, the Instacart-born pooler by the same author, and supports online resharding via logical replication. Releases ship weekly, and the project reports 2M+ queries per second across dozens of production deployments.
Why it's different — a real SQL parser keeps the Postgres features other poolers make you give up:
SETand Row-Level Security keep working: per-client session state is re-applied before each query.LISTEN/NOTIFYsurvive transaction pooling via in-process broadcast channels.- Transparent prepared statements, one Tokio process across all cores, and a PgBouncer-style admin database plus OpenMetrics.
The caveat: the more the proxy does, the more of your traffic architecture lives inside it — and its AGPL-3.0 license is the most restrictive here.
Good fit for: teams that want one proxy for pooling, routing, and scale-out, and want a company on the other end of the pager. PgDog is the only option here with a commercial vendor behind it.
Feature comparison
Project
| PgBouncer | Odyssey | PgDoorman | PgDog | |
|---|---|---|---|---|
| Language | C | C | Rust | Rust |
| License | ISC | BSD-3-Clause | MIT | AGPL-3.0 |
| GitHub stars | 4.2k | 3.5k | 0.3k | 5.1k |
| Commercial vendor | ❌ | ❌ | ❌ | ✅ |
Pooling — the last two rows are what transaction pooling usually breaks.
| PgBouncer | Odyssey | PgDoorman | PgDog | |
|---|---|---|---|---|
| Modes | Session, transaction, statement | Session, transaction | Session, transaction | Session, transaction |
| Multi-core | ❌ single-threaded | ✅ multi-threaded | ✅ multi-threaded, shared pool | ✅ multi-threaded |
| Prepared statements | ✅ opt-in (max_prepared_statements) | ✅ | ✅ transparent, with plan cache | ✅ |
Session state (SET, LISTEN/NOTIFY) | ❌ | ❌ | ❌ | ✅ |
Beyond pooling
| PgBouncer | Odyssey | PgDoorman | PgDog | |
|---|---|---|---|---|
| Built-in Prometheus metrics | ❌ external exporter | ✅ | ✅ | ✅ OpenMetrics |
| Load balancing / routing | ❌ | ✅ request routing | ❌ by design | ✅ |
| Sharding | ❌ | ❌ | ❌ by design | ✅ online resharding |
A ❌ in the last table isn't always a gap. PgDoorman deliberately stops at pooling — Ozon's position is that routing belongs in a different layer. The more rows a proxy fills in, the more of your traffic architecture lives inside it.
Our pick
One pooler per quadrant — the four don't compete head-on. Pick your row (pool only, or also route and shard?), then your column (production mileage, or a modern engine?). Two choices cover most teams:
Free: PgBouncer. The default for a reason: small, proven, and wired into every managed Postgres and operator you're likely to touch. Its one real limit is the single thread, and the fix is well-trodden — a few instances behind a load balancer.
Commercial: PgDog. The only pooler here with a company behind it, and it earns the spot on merit: pooling, load balancing, and sharding in one multi-threaded proxy that keeps SET, Row-Level Security, and LISTEN/NOTIFY working under transaction pooling.
Narrow constraints: Odyssey when one PgBouncer core saturates, PgDoorman when transaction pooling keeps fighting your drivers. Both are open-source only — no vendor to call.
Whichever you pick, watch for:
- Pooling mode. Session maximizes compatibility, transaction wins concurrency but breaks session state, statement is rarely what you want.
- Limits at the pooler. Per-user and per-database caps protect Postgres from thundering herds.
- Prepared statements. All four now handle them at the protocol level — opt-in on PgBouncer (
max_prepared_statements) — but test your own ORM rather than assuming. - HA, like any stateless proxy. Multiple poolers behind a VIP or load balancer, with health checks and drain/reload flows.
- The basics. Right-size the pool, the client cap, and timeouts against actual concurrency — pools are per user/db/host tuple.
Treat the pooler as part of your capacity and SLO strategy: cap concurrency, keep transactions short, and watch pool saturation.