PgBouncer es una de las mejoras con mejor ROI cuando escalas Odoo: menos conexiones reales a PostgreSQL, menos overhead, más estabilidad en picos. Pero para que funcione fino en Odoo 16, 17, 18 y 19, hay dos verdades importantes:
Odoo en producción necesita reverse proxy + WebSocket bien encaminado. Odoo+2Odoo+2
PgBouncer en pool_mode=transaction es ideal para web… pero rompe ciertas features de sesión (y ahí vive el 90% de los sustos). PgBouncer+1
Vamos al grano.
1) Lo que NO cambió (mucho) entre Odoo 16 → 19
En los docs oficiales de Odoo (16, 17, 18 y 19) se repite el mismo patrón de producción:
modo multi-proceso activado con --workers
cálculo recomendado de workers: (#CPU * 2) + 1
un worker dedicado para LiveChat/WebSocket en --gevent-port
y el proxy debe enrutar /websocket/ hacia ese puerto (y activar --proxy-mode) Odoo+3Odoo+3Odoo+3
En Odoo 19 incluso viene un ejemplo de Nginx donde location /websocket va al upstream de 8072 (gevent). Odoo
2) Arquitectura recomendada (simple, escalable y “anti-incendios”)
Topología típica (1 DB, 1 usuario DB):
Nginx/HAProxy (443)
→ Odoo HTTP workers 8069
→ Odoo WebSocket (LiveChat) 8072 (gevent_port) Odoo+1
→ PgBouncer 6432
→ PostgreSQL 5432
Por qué esta arquitectura funciona
El proxy separa tráfico “normal” (HTTP) del tráfico “long-lived” (WebSocket). Odoo lo recomienda explícitamente: /websocket/ debe ir al worker LiveChat en --gevent-port. Odoo+1
PgBouncer absorbe el “ruido” de conexiones (sobre todo con muchos workers) y limita el número de backends reales a Postgres.
3) Config mínima (copiar/pegar) para Odoo 16–19
Odoo (odoo.conf)
[options] proxy_mode = True workers = 8 max_cron_threads = 1 ; WebSocket / LiveChat gevent_port = 8072 longpolling_port = False
La guía oficial indica que en multi-processing hay un LiveChat worker en --gevent-port y que /websocket/ debe redirigirse ahí. Odoo+2Odoo+2
longpolling_port es un alias/deprecado y en la práctica se suele poner en False para evitar warnings. Odoo
Nginx (idea base)
/websocket/ → 8072
/ → 8069
Esto está alineado con el ejemplo oficial (Odoo 19) que redirige /websocket al upstream del gevent port. Odoo
4) PgBouncer para Odoo: el modo correcto (y el límite real)
Regla general
Para Odoo web: pool_mode = transaction.
PgBouncer define transaction pooling así: “una conexión server se asigna al cliente solo durante la transacción, y luego vuelve al pool”. GitHub
Ajustes base (PgBouncer)
pool_mode = transaction
default_pool_size (tu límite de concurrencia real por (db,user))
reserve_pool_size (picos cortos sin sobredimensionar)
max_client_conn (capacidad de entradas)
Esto te da un comportamiento predecible: si hay saturación, la ves como cola en PgBouncer (no como “Postgres murió”).
5) El “gotcha” #1: LISTEN/NOTIFY + transaction pooling
Aquí es donde mucha gente se quema.
PgBouncer documenta que transaction pooling rompe expectativas de sesión y solo funciona si la app “coopera” (no usa features no compatibles). PgBouncer+1
En particular, LISTEN es un ejemplo típico de feature de sesión que no encaja bien en transaction pooling (porque necesita mantener la misma conexión).
La OCA lo resume directamente para Odoo: las features de longpolling/tiempo real se apoyan en LISTEN/NOTIFY; NOTIFY pasa, pero LISTEN no en ese modo, porque LISTEN necesita mantener la conexión server abierta. Odoo Community
Soluciones que sí funcionan (elige una)
Opción A (la más práctica): “bypass” solo para la conexión que escucha
Mantienes Odoo → PgBouncer (transaction) para el 99% del tráfico
y dejas una conexión directa a Postgres para el “listener” (OCA propone ese enfoque). Odoo Community
Opción B: pool dedicado en pool_mode=session para lo que requiera sesión
Todo lo normal sigue en transaction
Pero lo “sesión-dependiente” va por session pooling (menos eficiente, pero compatible)
Resultado: estabilidad + features real-time sin sacrificar el pooling donde más rinde.
6) El “gotcha” #2: WebSocket mal encaminado (parece “bug”, es proxy)
En Odoo 16–19, si /websocket/ no llega al gevent_port, aparecen síntomas raros:
chat/Discuss sin “tiempo real”
notificaciones que no llegan
errores 400 en /websocket
La doc lo deja claro: necesitas proxy delante y redirigir /websocket/ al worker LiveChat, además de --proxy-mode. Odoo+2Odoo+2
7) Dimensionamiento rápido (que se sostiene en producción)
Los docs de Odoo (17–19, y también 16) dan reglas prácticas que puedes usar como base:
workers: (#CPU * 2) + 1
1 worker ≈ 6 usuarios concurrentes (regla orientativa)
estimación de RAM por worker “heavy/light” Odoo+3Odoo+3Odoo+3
Con eso defines workers, y luego dimensionas PgBouncer para controlar el backend real.
8) Seguridad mínima: HTTPS sí o sí
Odoo advierte que transmite credenciales en claro si no hay HTTPS, y recomienda desplegar SSL termination en el proxy. Odoo+1
En términos de resultado: sin HTTPS no hay “producción seria”, aunque la app “funcione”.
Cierre
Para Odoo 16–19, PgBouncer es un upgrade muy rentable si lo despliegas así:
Proxy bien hecho: /websocket/ → gevent_port
PgBouncer en transaction para el grueso del tráfico
Y una estrategia clara para features de sesión (LISTEN), ya sea bypass puntual o pool en session