Skip to Content

Relationship between Odoo workers and PgBouncer

November 25, 2025 by
Relationship between Odoo workers and PgBouncer
John Wolf
| No comments yet

When Odoo "slows down" under load, it is almost never a "mystery": it is usuallypoorly sized concurrencybetween:

  • Odoo Workers(how many requests you can handle in parallel)

  • Client connections to PgBouncer(how many simultaneous connections the pooler receives)

  • Server connections to PostgreSQL(how many actual connections PgBouncer opens to Postgres)

Understanding this chain allows you to scale without falling into"Connection Pool Is Full"or infinite queues.


1) The mental map: 3 levels of concurrency

Level A — Odoo: workers and crons

In production (Linux), Odoo recommends multi-process mode and provides a rule of thumb for workers:

workers ≈ (#CPU * 2) + 1. Odoo+1

Additionally, Odoo clarifies that in multi-processing adedicated worker is raisedfor LiveChat/WebSocket on --gevent-port (and the proxy must redirect /websocket/ to that port). Odoo

Translation:even if you configure workers = N, in practice there ismore parallel activity(web + cron + gevent).


Level B — Odoo: db_maxconn (client connection pool)

Odoo uses a connection pool to the DB and, when it runs short, the classic appears:

psycopg2.pool.PoolError: The Connection Pool Is Full GitHub+1

In multi-process, the most confusing point is this: the 'pool'.It is not global to all workers.Each process has its own dynamics, and with crons/long polling, you can saturate it even with values that 'seem' sufficient (there are several cases reported by the community). GitHub+1


Level C — PgBouncer: max_client_conn and default_pool_size.

PgBouncer clearly separates:

  • clients(Odoo → PgBouncer)

  • servers(PgBouncer → PostgreSQL)

And organizes pools by pair(user, database).This is where default_pool_size defines the maximum number of server connections per pool. PgBouncer+1

Intransaction pooling(the typical with Odoo), PgBouncer assigns a server connectiononly during the transactionand then returns it to the pool. PgBouncer


2) The key relationship (in one sentence)

More Odoo workers ⇒ more potential concurrency ⇒ more 'client' connections to PgBouncer ⇒ more pressure to open real connections to PostgreSQL (up to the limit of default_pool_size).

If you misconfigure any of these levels, you get:

  • queue in PgBouncer (clients waiting)

  • or saturation in Postgres (too many active connections)

  • or the 'pool is full' on the Odoo side.


3) What parameter controls what (no smoke)

Odoo

  • workers: how many simultaneous web requests you can run (approximately). Odoo+1

  • max_cron_threads: how many crons run in parallel (DB-active many times)

  • db_maxconn: "how many database connections the process can have available" (if you run short, it crashes) GitHub+1

PgBouncer

  • max_client_conn: how many connectionsclientaccepts in total (Odoo → PgBouncer)

  • default_pool_size: how many connectionsservermaximum per pool (db/user) PgBouncer+1

  • transaction pooling: server connection assignedonly during the transaction PgBouncer


4) How to size PgBouncer based on Odoo workers (step by step)

Step 1 — Estimate yourpeakof "active DB processes"

Starting point:

  • Web workers(N)

  • Cron threads(C)

  • Gevent/WebSocket(G ≈ 1 in multi-processing) Odoo

Potential active DBs ≈ N + C + G

Note: this is the "worst case". In reality, not everyone is in the DB at the same time, but it is useful for initial sizing.


Step 2 — Estimateclient connectionsto PgBouncer

Each process can request multiple connections if its pool allows it (db_maxconn). During peaks (or when there is long polling, heavy crons, and web traffic), usage approaches the limit and PoolErrors occur when it runs short. Odoo+1

Theoretical maximum of clients to PgBouncer:

max_client_conn_ideal ≥ (N + C + G) * db_maxconn + margin

  • The “margin” is for spikes, admin connections, health checks, tools, etc.


Step 3 — Sizeserver connectionsto Postgres (what really matters)

Here, default_pool_size is determined by (db, user). PgBouncer+1

In transaction pooling, default_pool_size behaves as:

limit of concurrent transactions per db/user PgBouncer+1

Rule of thumb:

  • default_pool_size should be high enough to ensurethere is no queueduring peak hours,

  • but low enough tonot overwhelm Postgreswith too many simultaneous queries.


Step 4 — Add controlled “burst”: reserve_pool_size

Instead of inflating default_pool_size, use reserve_pool_size for short spikes (10–20% of the pool is usually a reasonable starting point). This saves you from spikes without leaving Postgres “exposed”.


5) Real example (to understand the math)

Server with 8 CPUs:

  • Odoo suggests workers ≈ (8*2)+1 = 17 Odoo

  • max_cron_threads = 2

  • gevent worker = 1 Odoo

  • db_maxconn = 32


5.1 PgBouncer max_client_conn

Worst case:

  • active DB processes = 17 + 2 + 1 = 20

  • potential clients = 20 * 32 = 640

Initial recommendation:max_client_conn 1000–2000 (depending on your environment).


5.2 PgBouncer default_pool_size

If you have1 db and 1 user(the norm in Odoo), you have1 pool.

A “healthy” start is usually between 40 and 120 (depends on the power of Postgres and the quality of queries). Then you adjust by looking at queues and latency.


6) Signs that you sized it wrong

You fell short (queue in PgBouncer)

  • Slow users “in waves”

  • PgBouncer shows clients waiting (cl_waiting increases)

  • Postgres is NOT at 100%

→ Increase default_pool_size or adjust crons/queries.

You overshot (Postgres suffers)

  • Sustained high CPU/IO

  • more waits/locks

  • query latency increases

→ Decrease default_pool_size and optimize (indexes/queries) before “giving more connections”.

Odoo explodes with “The Connection Pool Is Full”

Indicates that the Odoo pool is insufficient for the actual concurrency pattern (web + cron + longpolling), something reported in scenarios with workers>0 and parallel crons. GitHub+1


7) Final recommendation (the approach that most avoids incidents)

  1. Size workers using the Odoo rule as a starting point. Odoo

  2. Keep db_maxconn moderate (32–64 is usually a common operational range; increase only with evidence).

  3. Set max_client_conn high (capacity) and control the backend with default_pool_size (protection). PgBouncer+1

  4. Use transaction pooling (Odoo-friendly) and measure queues. PgBouncer


Next chapter ->

Relationship between Odoo workers and PgBouncer
John Wolf November 25, 2025
Share this post
Tags
Archive
Sign in to leave a comment