Objective:go live without surprises (auth, pool, WebSocket, queues, logs, backups).
Tip:print this and use it as a 'gate' before opening 443.
Deployment details (fill in)
Project/Client: ___________________________
Date: ____ / ____ / ______
Responsible: _______________________________
Odoo Host: _________________________________
PgBouncer Host: ____________________________
PostgreSQL Host: ____________________________
Odoo Version: ________ PostgreSQL: ________ PgBouncer: ________
Target concurrent users: _____________
1) Network and exposure
OdooNOis exposed to the Internet (only loopback or private network)
Only the reverse proxy listens on443
Applied firewall:
public 443
8069/8072 closed to the public
6432 only from Odoo/internal network
5432 only from PgBouncer/bastion
Correct DNS/hostnames (no 'pointing to old IP')
2) Reverse proxy + WebSocket (critical)
/ routes to Odoo HTTP (8069)
/websocket/ routes to gevent (8072)
proxy_mode = True in Odoo
Headers: X-Forwarded-For, X-Forwarded-Proto, Host
Reasonable proxy timeouts (PDF/reports do not die due to timeout)
3) Odoo: minimum security
strong admin_passwd andoutof the repository
list_db = False
dbfilter defined (if multi-DB / multi-tenant applies)
separate addons_path (core vs custom)
Sensitive parameters only in secrets/ENV (no hardcode in git)
4) Odoo: performance and limits
workers sized by CPU/RAM (documented)
conservative max_cron_threads (1–2) unless evidence
Defined limits:
limit_time_cpu
limit_time_real
limit_memory_soft
limit_memory_hard
Heavy crons identified (list) and schedules controlled
Massive jobs use batching (avoid long transactions)
5) PgBouncer: correct pooling
pool_mode = transaction (for normal ORM traffic)
calculated default_pool_size (not "eyeballed")
reserve_pool_size configured for short spikes
sufficient max_client_conn for the peak (documented)
If multi-DB: limits max_db_connections / max_user_connections reviewed
RELOAD procedure tested (no downtime)
6) Auth: users, auth_file and rotation
defined auth_type (ideal: scram-sha-256)
auth_file (userlist.txt) with:
correct format "user" "secret"
permissions0600(correct owner)
If using auth_user/auth_query: technical role and query/function validated
Documented password rotation + test performed (change + reload + validation)
Trust is not used in production
7) TLS (if applicable)
TLS on 443 (proxy)
TLS between Odoo↔PgBouncerif crossing an untrusted network
db_sslmode defined (if applicable: verify-full with CA/hostname)
Certificates with correct SAN + expiration monitored
8) PostgreSQL: stability and protection
max_connections consistent with PgBouncer (not inflated without control)
Defined timeouts:
statement_timeout (clear policy)
lock_timeout
idle_in_transaction_session_timeout
Autovacuum/maintenance plan (monitoring + windows)
Minimum roles/permissions (do not use superuser for Odoo)
9) Minimum observability (before opening)
PgBouncer
Useful logs enabled:
log_connections=1
log_disconnections=1
log_pooler_errors=1
Verified commands:
SHOW POOLS; (queue: cl_waiting, maxwait)
SHOW STATS;
SHOW SERVERS; / SHOW CLIENTS;
Odoo
p95/p99 latency metrics (at least login + critical operation)
Alerts for errors (timeouts, deadlocks, “pool full”)
Duration of crons and overlaps monitored
Infra
CPU/RAM/swap
I/O wait/disk latency
network (drops/retransmits)
file descriptors (especially PgBouncer)
10) Backups and “day 2”
Configured automatic backups
Tested restore (actual RTO/RPO)
Documented upgrade plan (Odoo/Postgres/PgBouncer)
Basic runbook:
“if there is slowness” → check SHOW POOLS; + pg_stat_activity
“if auth fails” → check userlist.txt + RELOAD + logs
“if there is a queue” → identify long transactions/locks
Signatures (to close the gate)
Technical responsible: _______________________ Date://____
QA/Operations: ___________________________ Date://____
Aprobación final: __________________________ Fecha: //____