This document describes basic steps to set up Odoo in production. It follows installation, but should not be used for development systems.
dbfilter
Odoo is a multi-tenant system: a single Odoo system may run and serve a number of database instances. It is also highly customizable, with customizations (starting from the modules being loaded) depending on the "current database".
This is not an issue when working with the backend (web client) as a logged-in company user: the database can be selected when logging in, and customizations loaded afterwards.
However it is an issue for non-logged users (portal, website) which aren't bound to a database: Odoo need to know which database should be used for the operations or to get the data. If multi-tenancy is not used that is not an issue, there's only one database to use, but if there are multiple databases accessible Odoo needs a rule to know which one it should use.
That is one of the purposes of --db-filter
:
it specifies the default database for the Odoo system. The value is a
regular expression, possibly including the dynamically injected hostname
or subdomain through which the Odoo system is accessed.
If an Odoo hosts multiple databases in production, especially if website
is used, it must use a dbfilter or a number of features will not work
correctly or not use at all.
Configuration sample
- filtering only db with a name beginning with 'mycompany'
in /etc/odoo.conf
set:
[options]
dbfilter = ^mycompany.*$
- filtering only db with a name equal to hostname without domain
in /etc/odoo.conf
set:
[options]
dbfilter = %d
PostgreSQL
By default, PostgreSQL only allows connection over UNIX sockets and loopback connections (from "localhost", the same machine the PostgreSQL server is installed on).
UNIX socket is fine if you want Odoo and PostgreSQL to execute on the same machine, and is the default when no host is provided, but if you want Odoo and PostgreSQL to execute on different machines 1 it will need to listen to network interfaces 2, either:
- only accept loopback connections and use an SSH tunnel between the machine on which Odoo runs and the one on which PostgreSQL runs, then configure Odoo to connect to its end of the tunnel
- accept connections to the machine on which Odoo is installed, possibly over ssl (see PostgreSQL connection settings for details), then configure Odoo to connect over the network
Configuration sample
- allow tcp connection on localhost
- allow tcp connection from 192.168.1.x network
in /etc/postgresql/9.5/main/pg_hba.conf
set:
# IPv4 local connections:
host all all 127.0.0.1/32 md5
host all all 192.168.1.0/24 md5
in /etc/postgresql/9.5/main/postgresql.conf
set:
listen_addresses = 'localhost,192.168.1.2'
port = 5432
max_connections = 80
Configuring Odoo
Out of the box, Odoo connects to a local postgres over UNIX socket via port 5432. This can be overridden using the database options when your Postgres deployment is not local and/or does not use the installation defaults.
The packaged installers will automatically
create a new user (odoo
) and set it as the database user.
- The database management screens are protected by the
admin_passwd
setting. This setting can only be set using configuration files, and is simply checked before performing database alterations. It should be set to a randomly generated value to ensure third parties can not use this interface. - all database operations use the database options, including the database management
screen. For the database management screen to work requires that the PostgreSQL user
have
createdb
right. users can always drop databases they own. For the database management screen to be completely non-functional, the PostgreSQL user needs to be created with
no-createdb
and the database must be owned by a different PostgreSQL user.Warning
the PostgreSQL user must not be a superuser
Configuration sample
- connect to a PostgreSQL server on 192.168.1.2
- port 5432
- using an 'odoo' user account,
- with 'pwd' as a password
- filtering only db with a name beginning with 'mycompany'
in /etc/odoo.conf
set:
[options]
admin_passwd = mysupersecretpassword
db_host = 192.168.1.2
db_port = 5432
db_user = odoo
db_password = pwd
dbfilter = ^mycompany.*$
Builtin server
Odoo includes built-in HTTP servers, using either multithreading or multiprocessing.
For production use, it is recommended to use the multiprocessing server as it increases stability, makes somewhat better use of computing resources and can be better monitored and resource-restricted.
- Multiprocessing is enabled by configuring
a non-zero number of worker processes
, the number of workers should be based on the number of cores in the machine (possibly with some room for cron workers depending on how much cron work is predicted) - Worker limits can be configured based on the hardware configuration to avoid resources exhaustion
Warning
multiprocessing mode currently isn't available on Windows
Worker number calculation
- Rule of thumb : (#CPU * 2) + 1
- Cron workers need CPU
- 1 worker ~= 6 concurrent users
memory size calculation
- We consider 20% of the requests are heavy requests, while 80% are simpler ones
- A heavy worker, when all computed field are well designed, SQL requests are well designed, ... is estimated to consume around 1Go of RAM
- A lighter worker, in the same scenario, is estimated to consume around 150MB of RAM
Needed RAM = #worker * ( (light_worker_ratio * light_worker_ram_estimation) + (heavy_worker_ratio * heavy_worker_ram_estimation) )
LiveChat
In multiprocessing, a dedicated LiveChat worker is automatically started and
listening on the longpolling port
but
the client will not connect to it.
Instead you must have a proxy redirecting requests whose URL starts with
/longpolling/
to the longpolling port. Other request should be proxied to
the normal HTTP port
Configuration sample
- Server with 4 CPU, 8 Thread
- 60 concurrent users
- 60 users / 6 = 10 <- theorical number of worker needed
- (4 * 2) + 1 = 9 <- theorical maximal number of worker
- We'll use 8 workers + 1 for cron. We'll also use a monitoring system to measure cpu load, and check if it's between 7 and 7.5 .
- RAM = 9 * ((0.8*150) + (0.2*1024)) ~= 3Go RAM for Odoo
in /etc/odoo.conf
:
[options]
limit_memory_hard = 1677721600
limit_memory_soft = 629145600
limit_request = 8192
limit_time_cpu = 600
limit_time_real = 1200
max_cron_threads = 1
workers = 8
HTTPS
Whether it's accessed via website/web client or the webservice, Odoo transmits authentication information in cleartext. This means a secure deployment of Odoo must use HTTPS3. SSL termination can be implemented via just about any SSL termination proxy, but requires the following setup:
- enable Odoo's
proxy mode
. This should only be enabled when Odoo is behind a reverse proxy - set up the SSL termination proxy (Nginx termination example)
- set up the proxying itself (Nginx proxying example)
- your SSL termination proxy should also automatically redirect non-secure
connections to the secure port
Warning
In case you are using the Point of Sale module in combination with a POSBox,
you must disable the HTTPS configuration for the route /pos/web
to avoid
mixed-content errors.
Configuration sample
- redirect http requests to https
- proxy requests to odoo
in /etc/odoo.conf
set:
proxy_mode = True
in /etc/nginx/sites-enabled/odoo.conf
set:
#odoo server
upstream odoo {
server 127.0.0.1:8069;
}
upstream odoochat {
server 127.0.0.1:8072;
}
# http -> https
server {
listen 80;
server_name odoo.mycompany.com;
rewrite ^(.*) https://$host$1 permanent;
}
server {
listen 443;
server_name odoo.mycompany.com;
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;
# Add Headers for odoo proxy mode
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_set_header X-Real-IP $remote_addr;
# SSL parameters
ssl on;
ssl_certificate /etc/ssl/nginx/server.crt;
ssl_certificate_key /etc/ssl/nginx/server.key;
ssl_session_timeout 30m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
# log
access_log /var/log/nginx/odoo.access.log;
error_log /var/log/nginx/odoo.error.log;
# Redirect requests to odoo backend server
location / {
proxy_redirect off;
proxy_pass http://odoo;
}
location /longpolling {
proxy_pass http://odoochat;
}
# common gzip
gzip_types text/css text/less text/plain text/xml application/xml application/json application/javascript;
gzip on;
}
Odoo as a WSGI Application
It is also possible to mount Odoo as a standard WSGI application. Odoo
provides the base for a WSGI launcher script as openerp-wsgi.py
. That
script should be customized (possibly after copying it) to correctly set the
configuration directly in openerp.tools.config
rather than through the
command-line or a configuration file.
However the WSGI server will only expose the main HTTP endpoint for the web client, website and webservice API. Because Odoo does not control the creation of workers anymore it can not setup cron or livechat workers
Cron Workers
To run cron jobs for an Odoo deployment as a WSGI application requires
- a classical Odoo (run via
odoo.py
) - connected to the database in which cron jobs have to be run (via
odoo.py -d
) - which should not be exposed to the network. To ensure cron runners are not
network-accessible, it is possible to disable the built-in HTTP server
entirely with
odoo.py --no-xmlrpc
or settingxmlrpc = False
in the configuration file
LiveChat
The second problematic subsystem for WSGI deployments is the LiveChat: where most HTTP connections are relatively short and quickly free up their worker process for the next request, LiveChat require a long-lived connection for each client in order to implement near-real-time notifications.
This is in conflict with the process-based worker model, as it will tie up worker processes and prevent new users from accessing the system. However, those long-lived connections do very little and mostly stay parked waiting for notifications.
The solutions to support livechat/motifications in a WSGI application are:
- deploy a threaded version of Odoo (instread of a process-based preforking
one) and redirect only requests to URLs starting with
/longpolling/
to that Odoo, this is the simplest and the longpolling URL can double up as the cron instance. - deploy an evented Odoo via
openerp-gevent
and proxy requests starting with/longpolling/
tothe longpolling port
.
Serving Static Files
For development convenience, Odoo directly serves all static files in its modules. This may not be ideal when it comes to performances, and static files should generally be served by a static HTTP server.
Odoo static files live in each module's static/
folder, so static files
can be served by intercepting all requests to /MODULE/static/FILE
,
and looking up the right module (and file) in the various addons paths.
Security
"Super-admin" password
Configuring Odoo mentioned admin_passwd
in passing.
This setting is used on all database management screens (to create, delete, dump or restore databases).
If the management screens must not be accessible, or must only be accessible
from a selected set of machines, use the proxy server's features to block
access to all routes starting with /web/database
except (maybe)
/web/database/selector
which displays the database-selection screen.
If the database-management screen should be left accessible, the
admin_passwd
setting must be changed from its admin
default: this
password is checked before allowing database-alteration operations.
It should be stored securely, and should be generated randomly e.g.
$ python -c 'import base64, os; print(base64.b64encode(os.urandom(24)))'
which will generate a 32 characters pseudorandom printable string.