Deploying Odoo in production: PostgreSQL, workers and reverse proxy
A hardened Odoo deployment: sizing workers, tuning PostgreSQL, nginx reverse proxy with websockets, backups and the security settings most installs miss.
Odoo is easy to install and easy to deploy badly. A production install needs four things right: worker sizing, PostgreSQL tuning, a correct reverse proxy, and locked-down database management.
Worker sizing
Odoo in multi-processing mode forks worker processes. A workable starting formula is workers = (CPU cores * 2) + 1, plus dedicated cron workers.
; /etc/odoo/odoo.conf
[options]
admin_passwd = <long-random-string>
db_host = 127.0.0.1
db_user = odoo
db_password = <secret>
dbfilter = ^mycompany$
list_db = False
proxy_mode = True
workers = 9
max_cron_threads = 2
limit_memory_soft = 2147483648
limit_memory_hard = 2684354560
limit_time_cpu = 600
limit_time_real = 1200
limit_time_real must exceed your longest legitimate request (large invoicing or MRP runs), or users will see random 504s.
PostgreSQL
- Put the database on its own volume with fast storage; Odoo is chatty.
shared_buffers≈ 25% RAM,effective_cache_size≈ 60–70% RAM,work_mem32–64MB for reporting-heavy installs.- Enable
pg_stat_statements— nearly every Odoo performance ticket ends there. - Schedule
VACUUM/autovacuum tuning for high-churn tables such asmail_messageandir_attachment.
nginx reverse proxy
upstream odoo { server 127.0.0.1:8069; }
upstream odoochat { server 127.0.0.1:8072; }
server {
listen 443 ssl http2;
server_name erp.example.com;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 720s;
client_max_body_size 100m;
location /websocket { proxy_pass http://odoochat; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
location / { proxy_pass http://odoo; }
location ~* /web/static/ { proxy_cache_valid 200 60m; proxy_pass http://odoo; expires 864000; }
}
Without the websocket block, live chat, discuss notifications and the barcode app silently stop updating.
Security musts
list_db = Falseand a strongadmin_passwd— the database manager is the number one Odoo breach vector.dbfilterso one hostname maps to exactly one database.- Run Odoo as a non-root system user with the filestore on a dedicated path.
- Block
/web/database/*at the proxy for public installs.
Backups
A backup is both the PostgreSQL dump and the filestore directory (~/.local/share/Odoo/filestore/<db>). Restoring one without the other gives you an ERP with no attachments. Test a full restore quarterly and record the elapsed time — that is your real RTO.
Upgrades
Odoo releases annually and Community upgrades are your responsibility. Use the official upgrade service or odoo-bin --upgrade path on a clone first, and budget most of the effort for custom modules and OCA dependencies rather than for core data.