Odoo server configuration: odoo.conf, workers and Nginx

Configure an Odoo server for production: the odoo.conf options that matter, how many workers and how much RAM you need, memory and time limits, and an Nginx reverse proxy.

Updated . 10 min read

A fresh Odoo install runs in a single process, which is fine for testing and painful for a team. This guide covers the odoo.conf settings that matter in production: how many workers to run, how much RAM and CPU you need, the limits that keep one heavy request from taking the server down, and the Nginx setup that goes with it. It applies to Odoo 16 through 19.

Where Odoo reads its configuration

Odoo takes its settings from a config file passed with -c (for example odoo-bin -c /etc/odoo.conf), and any command-line flag overrides the file. All options live under a single [options] section. If you followed how to install Odoo, your file is /etc/odoo.conf and you apply changes with sudo systemctl restart odoo.

Server requirements and sizing

Odoo's deployment documentation gives a rule of thumb that holds up well in practice. Each worker handles roughly 6 concurrent users, and the theoretical maximum worker count is (CPU cores × 2) + 1. For memory, assume 80% of requests are light (about 150 MB) and 20% are heavy (about 1 GB):

RAM estimate
RAM needed ≈ workers × ((0.8 × 150 MB) + (0.2 × 1024 MB))
           ≈ workers × 325 MB

A worked example for a 4-core server: up to 9 workers, which needs about 9 × 325 MB ≈ 2.9 GB for Odoo itself. Add PostgreSQL and the operating system on top.

Team size (concurrent users)WorkersCPU coresRAM (Odoo + PostgreSQL)
Up to 10224 GB
10 to 304 to 52 to 48 GB
30 to 606 to 948 to 16 GB
60+10+6+16 GB+, often a separate database server

Treat these as starting points. Heavy reports, large imports, the website builder and many scheduled actions all push the numbers up, so watch real CPU and memory use after go-live.

Workers and cron threads

Setting workers above 0 switches Odoo into multi-process mode: a pool of HTTP worker processes, separate cron workers for scheduled actions, and a gevent process for live chat and notifications.

  • workers: number of HTTP worker processes. 0 means single-process mode (development only).
  • max_cron_threads: workers reserved for scheduled actions. The default of 2 suits most databases; use 1 on very small servers.
  • gevent_port: the port for the websocket/long-polling process, 8072 by default. Nginx must route /websocket to it (see below), or notifications and chat stop updating live.
  • db_maxconn: the maximum PostgreSQL connections per process. Keep (workers + max_cron_threads) × db_maxconn below PostgreSQL's max_connections.

Memory and time limits

Limits recycle a worker that grows too large or a request that runs too long, so one runaway report doesn't starve everyone else. They only apply in multi-process mode.

OptionDefaultWhat it does
limit_memory_soft2048 MBAfter a request, a worker above this size is gracefully recycled.
limit_memory_hard2560 MBA worker that hits this size is killed immediately.
limit_time_cpu60 sMaximum CPU time a single request may use.
limit_time_real120 sMaximum wall-clock time for a single request.
limit_requestDepends on versionRequests a worker serves before it is recycled.

Memory limits are set in bytes in odoo.conf. If large reports or imports time out, raise limit_time_cpu and limit_time_real moderately rather than removing the limits.

Database and security settings

  • admin_passwd: the master password for creating, duplicating, restoring and dropping databases. Use a long random value and keep it out of version control.
  • list_db = False: hides the database manager and database list from the public. Recommended in production.
  • dbfilter: restricts which databases a hostname can open, for example ^%d$ to match the first part of the domain. Essential when one server hosts several databases.
  • proxy_mode = True: trust the X-Forwarded-* headers from your reverse proxy so Odoo sees the real client IP and HTTPS scheme. Only enable it behind a proxy.
  • http_interface = 127.0.0.1: listen only on localhost so users can't bypass Nginx on port 8069.

A complete production odoo.conf

For a 4-core, 8 GB server serving around 30 to 40 concurrent users. With 6 workers and 1 cron thread, (6 + 1) × 12 = 84 database connections stays under PostgreSQL's default limit of 100.

/etc/odoo.conf
[options]
admin_passwd = replace-with-a-long-random-password
db_host = False
db_port = False
db_user = odoo
db_password = False
db_maxconn = 12
list_db = False
dbfilter = ^%d$

addons_path = /opt/odoo/odoo/addons,/opt/odoo/custom-addons
data_dir = /opt/odoo/.local/share/Odoo
logfile = /var/log/odoo/odoo.log

proxy_mode = True
http_interface = 127.0.0.1
http_port = 8069
gevent_port = 8072

workers = 6
max_cron_threads = 1

; 2 GB soft, 2.5 GB hard (in bytes)
limit_memory_soft = 2147483648
limit_memory_hard = 2684354560
limit_time_cpu = 120
limit_time_real = 240

Nginx reverse proxy with websockets

With workers enabled, Odoo serves pages on port 8069 and websockets on the gevent port, 8072. Nginx sends each to the right place and terminates SSL.

/etc/nginx/sites-available/odoo
upstream odoo {
  server 127.0.0.1:8069;
}
upstream odoochat {
  server 127.0.0.1:8072;
}
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  server_name erp.yourcompany.com;

  client_max_body_size 100m;
  proxy_read_timeout 720s;
  proxy_connect_timeout 720s;
  proxy_send_timeout 720s;

  location /websocket {
    proxy_pass http://odoochat;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
  }

  location / {
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_redirect off;
    proxy_pass http://odoo;
  }

  gzip on;
  gzip_types text/css text/plain application/json application/javascript;
}

Then run sudo certbot --nginx -d erp.yourcompany.com to add HTTPS, and restart Odoo so the new odoo.conf takes effect.

Production checklist

  • Workers and limits sized for your users, and checked against real usage after go-live.
  • Database manager hidden (list_db = False) and a strong master password.
  • Odoo bound to localhost, with only ports 22, 80 and 443 open in the firewall.
  • Daily off-server backups of the database and filestore, plus a tested restore.
  • Monitoring and alerts for CPU, memory, disk space and uptime.
  • A staging copy for testing module updates before they reach production.

Don't want to tune this yourself? On EasyInstance managed Odoo hosting, workers, proxying, SSL and backups are configured for you. You choose how many workers you need and change it from the dashboard, with billing at $30 per worker a month. See pricing.

Related

New server? Start with how to install Odoo on Ubuntu. Weighing your options? Read how to host Odoo.

Rather not run the server yourself?

Get managed Odoo hosting with backups, SSL, monitoring and updates handled for you. Free for 10 days.