How Odoo's dbfilter picks which databases a request can reach, the regex traps to avoid, and how OCA's dbfilter_from_header lets Nginx map custom domains to databases.
Odoo is a multi-tenant system: a single Odoo server can run and serve several databases. The dbfilter option decides which of those databases a given request can reach.
The official deployment documentation has a good introduction to database filtering. This article goes into how it works in practice, the traps to avoid, and what to do when domain names and database names do not line up.
How dbfilter Works
dbfilter is a regular expression built dynamically for each request. You can use the variables %d and %h to adapt it to the Host header of the HTTP request:
%dis replaced by the first subdomain afterwww:https://demo.example.com:%d=demohttps://www.demo.example.com:%d=demo
%his replaced by the full domain, without the port if there is one:https://demo.example.com:%h=demo.example.comhttps://demo.example.com:8080:%h=demo.example.com
So if you define dbfilter = ^%d.*\Z, a user visiting https://demo.example.com can access the databases matching ^demo.*\Z, for example:
demodemo_backup_20180404
Beware of Permissive Patterns
Odoo filters the database list with re.match:
dbs = [i for i in dbs if re.match(r, i)]
re.match anchors the pattern at the start of the name but not at the end, so it is easy to write a regex that is more permissive than intended. If you define dbfilter = demo_*, for example, it also matches demoinefromage_prod.
When Domains and Database Names Do Not Match: dbfilter_from_header
You cannot always derive database names from domain names.
For example, a single Odoo instance might serve two databases:
customer1_prodcustomer2_prod
But each customer wants its own custom domain:
https://back.customer1.comhttps://app.customer2.com
To support this case, you can use OCA’s dbfilter_from_header module.
Installation
git clone https://github.com/OCA/server-tools -b 9.0 --no-checkout --single-branch --depth 1
cd server-tools
git config core.sparsecheckout true
echo "dbfilter_from_header" > .git/info/sparse-checkout
git read-tree -mu HEAD
Configuration
On the Nginx side, define a dedicated database filter for each virtual host. In our example, for https://back.customer1.com:
server_name back.customer1.com;
(...)
location / {
(...)
proxy_set_header X-Odoo-dbfilter ^customer1_.*\Z;
}
Two things to note:
- before passing the request to Odoo, Nginx takes all HTTP headers, lowercases them and converts dashes to underscores;
- we use
\Zinstead of$to match the end of the string, because Nginx cannot escape$.
Two Levels of Filtering
dbfilter_from_header respects the dbfilter defined at Odoo level, so you can set up two levels of filtering. In the Odoo configuration:
dbfilter = ^.+prod.*$
And in Nginx:
proxy_set_header X-Odoo-dbfilter ^.*customer1.*\Z;