Skip to Content

How to calculate default_pool_size

November 24, 2025 by
How to calculate default_pool_size
John Wolf
| No comments yet

default_pool_size is one of those parameters that, if you set it "by eye", might work… until the day the load increases and it blows up:Postgres saturated(if you overdo it) orqueues in PgBouncer(if you fall short).

The good news: it can be sized with a simple method and then fine-tuned with metrics.


1) What really controls default_pool_size

PgBouncer defines default_pool_size as:

how many "server" connections it allows per user/database pair(user/database pair), and it can be overridden by DB. Postgres Professional

Intransaction pooling(the typical case with Odoo), a "server" connection is assignedonly during the transactionand then returns to the pool. Postgres Professional+1

Translation:default_pool_size is, in practice, your limit ofconcurrent transactionsto PostgreSQLper pool.


2) Before calculating: define how many "pools" you have (this changes everything)

Since the pool is by(db, user), the number of pools depends on how you connect:

  • Typical Odoo:1 DB user (odoo) + 1 DB ⇒1 pool(the most common).

  • Multi-tenant:1 DB user + N databases ⇒N pools(one per database).

  • Multiple DB users:(rare in Odoo) ⇒ you multiply pools (db × users).


3) Step by step to scale (without magic)

Step A — Define your real connection “budget” in PostgreSQL

Do not use max_connections as “all for PgBouncer.” Leave margin for:

  • superuser / admin

  • migrations, backups, maintenance

  • monitoring

  • replication (if applicable)

  • direct connections (if you decide to bypass for something specific)

Mental example:leave 10–20% freeas a buffer.


Step B — Distribute the budget among pools

Base formula:

default_pool_size_initial ≈ floor( pg_budget / num_pools )

Example:

  • max_connections = 300

  • reserve 50 for “everything else” →pg_budget = 250

  • you have 1 pool → default_pool_size ≈ 250 (but be careful: it’s not always advisable to go that high)


Step C — Do not give Postgres more concurrency than it can handle

Even if it “fits” in max_connections, Postgres can become slow if you give it too many simultaneous queries (CPU, I/O, locks). Therefore, as an operational rule:

  • Startconservatively(e.g. 20–80 per pool) and increase with metrics.

  • Sometimes 40 well-used connections perform better than 200 competing wildly.

default_pool_size is “how many” connections; it does not guarantee “more performance.”


4) Recommended method (the one that works in production)

4.1 Choose an initial default_pool_size per profile

If it is 1 pool (1 DB, 1 user):

  • Small server / moderate load:20–40

  • Medium server:40–80

  • Large server (powerful DB + optimized queries):80–150

If it is multi-DB (N pools):

  • split the budget by N andcapper pool (don't let one DB "eat" everything).

Remember: default_pool_size is per user/DB. Postgres Professional


4.2 Enable "burst control": reserve_pool_size

PgBouncer allows a temporary "extra" with:

  • reserve_pool_size (additional connections)

  • reserve_pool_timeout (when they are enabled if a client waits) Postgres Professional+1

Typical example for Odoo:

  • reserve_pool_size = 10 (or 10–20% of the pool)

  • reserve_pool_timeout = 5

This absorbs short spikes without over-provisioning the "normal" pool.


5) Use global limits to avoid surprises

Although default_pool_size is per (db,user), you can impose ceilings with:

This is gold if you have multiple DBs or scenarios where a pool can grow more than expected.


6) The real validation (the only one that matters)

Signal 1: Is there a queue in PgBouncer?

Look at SHOW POOLS; and pay attention to:

  • cl_waiting (clients waiting)

  • sv_active (active server connections)

If cl_waiting spikesandPostgres is comfortable → you probably lack pool.

Signal 2: Is Postgres “happy” or saturated?

If when increasing default_pool_size you see:

  • CPU/IO at 100%

  • more lock waits

  • more latency per query

…your DB does not benefit from more concurrency: you need to optimize queries/indexes or lower the pool.


7) Example “by the book” (typical Odoo)

Scenario

  • 1 DB, 1 user (odoo)

  • Postgres max_connections = 300

  • Reserves 60 (admin, maintenance, etc.) → budget = 240

  • You start with something prudent:

[pgbouncer]
pool_mode = transaction

default_pool_size = 80
reserve_pool_size = 20
reserve_pool_timeout = 5
max_db_connections = 120

Then:

  • if cl_waiting appears during peak hours and Postgres is not suffering → increase to 100–120

  • if Postgres is suffering → decrease and optimize


8) Bonus: don't forget max_client_conn (and FDs)

max_client_conn is how many clients (Odoo workers + connections) PgBouncer accepts, and the documentation itself warns that this impacts file descriptors and even provides formulas for the theoretical maximum. Postgres Professional

It is not directly part of the calculation of default_pool_size, but if you keep it low, you will see rejections even if the pool is fine.


Conclusion: the golden rule

default_pool_size is not “guessed”: it ischosen conservatively, itis observed(queues vs saturation) and itis adjusted.


Next chapter ->

How to calculate default_pool_size
John Wolf November 24, 2025
Share this post
Tags
Archive
Sign in to leave a comment