Odoo Multi-Tenant Architecture — Hosting Multiple Clients
Host Odoo for multiple clients — or multiple business units — on the same infrastructure. Three approaches compared: multi-company, multi-database, and full container isolation. Code examples included for each.
Three Approaches to Multi-Tenancy in Odoo
Odoo supports multi-tenancy, but it does not give you a single “multi-tenant” switch to flip. Instead, you choose from three approaches, each with different tradeoffs in isolation, complexity, and cost.
| Multi-Company | Multi-Database | Full Isolation | |
|---|---|---|---|
| Architecture | One database, one Odoo instance | Multiple databases, one Odoo instance | Separate containers per tenant |
| Data isolation | None (shared tables, access rules only) | Database-level (separate PostgreSQL databases) | Full (separate containers + databases) |
| Version independence | No — all tenants share one Odoo version | No — all tenants share one Odoo binary | Yes — each tenant can run different versions |
| Resource isolation | None | None (shared CPU/RAM/workers) | Full (Docker resource limits per tenant) |
| Custom modules | Shared (all tenants see all modules) | Shared binary, per-database module install | Fully independent per tenant |
| Scaling | Vertical only | Vertical only | Horizontal — add servers per tenant |
| Setup complexity | Low | Medium | High |
| Cost per tenant | Lowest | Low | Moderate |
| Best for | Internal departments, related companies | Hosting provider with similar clients | MSP/SaaS with enterprise clients |
The right choice depends on how much isolation your tenants need. Internal departments that trust each other? Multi-company is fine. External paying clients who expect their data cannot leak? Full isolation or nothing.
Multi-Company (Single Database) — When It Works
Odoo has built-in multi-company support. You create multiple companies within a single Odoo database and use record rules to control which users see which data.
How it works
- •One Odoo instance, one PostgreSQL database
- •Companies are created in Settings > Companies
- •Users can be assigned to one or multiple companies
- •Record rules filter data per company (sales orders, invoices, inventory)
- •Some data is shared: product templates, contacts (configurable), user accounts
When to use it
- •Holding companies with subsidiaries that share operations
- •A parent company with regional offices
- •Internal departments that need separate accounting but shared products
- •Any scenario where tenants trust each other and share a single Odoo admin
When NOT to use it
- •External clients who must not see each other's data
- •Tenants who need different Odoo versions or different module sets
- •Scenarios where one tenant's heavy load should not affect another
- •Compliance requirements that mandate database-level isolation
Configuration
No Docker or Nginx changes needed. Standard single-instance deployment:
# Standard docker-compose.yml — nothing multi-tenant-specific
version: "3.8"
services:
odoo:
image: odoo:18.0
ports:
- "8069:8069"
environment:
- HOST=db
- USER=odoo
- PASSWORD=odoo_db_password
volumes:
- odoo-data:/var/lib/odoo
depends_on:
- db
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=odoo
- POSTGRES_PASSWORD=odoo_db_password
volumes:
- db-data:/var/lib/postgresql/data
volumes:
odoo-data:
db-data:Multi-company is configured entirely inside Odoo's admin interface. The infrastructure is a standard single-instance deployment.
Pros
Simplest to set up. Lowest resource usage. Shared user accounts across companies. Shared master data (products, contacts) reduces duplication.
Cons
Zero data isolation at the infrastructure level. A bug or misconfigured record rule can expose data across companies. One bad query slows everyone. Cannot run different Odoo versions per company.
Multi-Database (Single Odoo Instance) — The Middle Ground
One Odoo process serves multiple databases. Each database is a completely separate Odoo instance with its own modules, users, and data. Nginx routes requests to the correct database based on subdomain or URL.
How it works
- •One Odoo binary, one set of workers
- •Multiple PostgreSQL databases, each containing a full Odoo installation
- •
db_filterinodoo.confmaps hostnames to databases - •Users log in through different URLs and are routed to their database
odoo.conf for multi-database
[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo_db_password
; Route databases by subdomain
; client1.example.com → database "client1"
; client2.example.com → database "client2"
dbfilter = ^%d$
; Disable database listing and management from the web UI
list_db = False
db_name = False
proxy_mode = True
workers = 6
max_cron_threads = 2
; Important: limit memory per worker, not per database
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_time_cpu = 600
limit_time_real = 1200The dbfilter = ^%d$ directive extracts the subdomain from the request hostname and uses it as the database name. A request to client1.example.com maps to database client1.
Other common patterns:
; Match full hostname (acme.example.com → acme)
dbfilter = ^%d$
; Match first part of hostname with a prefix (odoo-acme → acme)
dbfilter = ^odoo-%d$
; Hardcode to a single database (disable multi-database)
dbfilter = ^production_db$Nginx for subdomain routing
# Wildcard SSL — covers *.example.com
server {
listen 443 ssl http2;
server_name ~^(?<tenant>.+)\.example\.com$;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Proxy headers — Odoo uses Host header for dbfilter
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Odoo web client
location / {
proxy_pass http://127.0.0.1:8069;
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;
client_max_body_size 200m;
}
# Longpolling (live chat, discuss, notifications)
location /websocket {
proxy_pass http://127.0.0.1:8072;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
# Static files — serve directly for performance
location ~* /web/static/ {
proxy_pass http://127.0.0.1:8069;
proxy_cache_valid 200 90m;
proxy_buffering on;
expires 7d;
}
}
# HTTP → HTTPS redirect
server {
listen 80;
server_name *.example.com;
return 301 https://$host$request_uri;
}Wildcard SSL with Let's Encrypt
Subdomain routing requires a wildcard SSL certificate. Let's Encrypt supports this via DNS-01 challenge:
# Using certbot with Cloudflare DNS plugin
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d "example.com" \
-d "*.example.com"Docker Compose for multi-database
version: "3.8"
services:
odoo:
image: odoo:18.0
container_name: odoo-multitenant
depends_on:
- db
ports:
- "127.0.0.1:8069:8069"
- "127.0.0.1:8072:8072"
volumes:
- odoo-data:/var/lib/odoo
- ./config/odoo.conf:/etc/odoo/odoo.conf:ro
- ./custom-addons:/mnt/extra-addons
restart: unless-stopped
db:
image: postgres:16-alpine
container_name: odoo-db
environment:
- POSTGRES_USER=odoo
- POSTGRES_PASSWORD=odoo_db_password
volumes:
- db-data:/var/lib/postgresql/data
restart: unless-stopped
nginx:
image: nginx:alpine
container_name: odoo-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on:
- odoo
restart: unless-stopped
volumes:
odoo-data:
db-data:Creating new tenant databases
# Create a new database for a tenant via CLI
docker exec odoo-multitenant odoo -d client3 --init=base --stop-after-init
# Or use the Odoo database manager API (if enabled)
curl -X POST https://client3.example.com/web/database/create \
-d "master_pwd=your_master_password&name=client3&login=admin&password=admin_pass&lang=en_US"Pros
Real data isolation — each tenant has its own database with its own users and data. Per-tenant backups are straightforward (pg_dump per database). Moderate resource efficiency — shared Odoo binary and workers.
Cons
All tenants share the same Odoo version. All tenants share CPU and memory — one tenant's heavy report can starve others. Custom modules are shared across the binary. The dbfilter regex can be tricky to debug.
Full Isolation (Separate Containers per Client) — Production Multi-Tenant
Each tenant gets their own Odoo container, their own PostgreSQL container (or dedicated database user), and their own resource limits. This is what hosting providers and MSPs use for paying clients.
Architecture
┌─────────────────────────┐
│ Nginx / Traefik │
│ (SSL + routing) │
└──────┴──────┴──────┴─────┘
│ │ │
┌───────────┘ │ └───────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ client1-odoo │ │ client2-odoo │ │ client3-odoo │
│ (Odoo 18.0) │ │ (Odoo 18.0) │ │ (Odoo 17.0) │
│ 2GB RAM, 1 CPU │ │ 4GB RAM, 2 CPU │ │ 2GB RAM, 1 CPU │
└────────┴─────────┘ └────────┴─────────┘ └────────┴─────────┘
│ │ │
┌────────┴─────────┐ ┌───────┴──────────┐ ┌───────┴──────────┐
│ client1-db │ │ client2-db │ │ client3-db │
│ (PostgreSQL 16) │ │ (PostgreSQL 16) │ │ (PostgreSQL 16) │
└──────────────────┘ └──────────────────┘ └──────────────────┘Docker Compose per tenant
Each tenant gets their own compose file. Here is client1/docker-compose.yml:
version: "3.8"
services:
odoo:
image: odoo:18.0
container_name: client1-odoo
depends_on:
- db
expose:
- "8069"
- "8072"
volumes:
- odoo-data:/var/lib/odoo
- ./config/odoo.conf:/etc/odoo/odoo.conf:ro
- ./custom-addons:/mnt/extra-addons
environment:
- HOST=db
- USER=client1
- PASSWORD=${DB_PASSWORD}
deploy:
resources:
limits:
memory: 2G
cpus: "1.0"
reservations:
memory: 1G
cpus: "0.5"
networks:
- internal
- proxy
restart: unless-stopped
db:
image: postgres:16-alpine
container_name: client1-db
environment:
- POSTGRES_USER=client1
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- db-data:/var/lib/postgresql/data
deploy:
resources:
limits:
memory: 1G
cpus: "0.5"
networks:
- internal
restart: unless-stopped
volumes:
odoo-data:
db-data:
networks:
internal:
driver: bridge
proxy:
external: true
name: proxy-networkKey details:
- •No published ports. Each tenant's Odoo is only accessible through the shared proxy network. Tenants cannot reach each other's containers.
- •Separate Docker networks. The
internalnetwork connects Odoo to its own PostgreSQL only. Theproxynetwork connects Odoo to the Nginx/Traefik reverse proxy. - •Resource limits. Each tenant has hard caps on memory and CPU. One tenant cannot consume another's resources.
Traefik for automatic routing
Traefik is generally a better fit than Nginx for full-isolation setups because it discovers containers automatically via Docker labels. No Nginx config file per tenant.
version: "3.8"
services:
traefik:
image: traefik:v3.0
container_name: traefik
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
networks:
- proxy-network
restart: unless-stopped
volumes:
letsencrypt:
networks:
proxy-network:
name: proxy-networkThen add labels to each tenant's Odoo service:
# In client1/docker-compose.yml, add to the odoo service:
services:
odoo:
labels:
- "traefik.enable=true"
- "traefik.http.routers.client1.rule=Host(`client1.example.com`)"
- "traefik.http.routers.client1.entrypoints=websecure"
- "traefik.http.routers.client1.tls.certresolver=letsencrypt"
- "traefik.http.services.client1.loadbalancer.server.port=8069"Traefik sees the labels, routes client1.example.com to that container, and provisions SSL automatically. Adding a new tenant means deploying a new compose file with different labels. No proxy config to edit.
Database Configuration for Multi-Tenant
Separate PostgreSQL roles per tenant
Even when sharing a single PostgreSQL server (to save resources), create separate roles:
-- Create a role per tenant with strict isolation
CREATE ROLE client1 WITH LOGIN PASSWORD 'client1_secure_pass';
CREATE DATABASE client1_odoo OWNER client1;
REVOKE ALL ON DATABASE client1_odoo FROM PUBLIC;
CREATE ROLE client2 WITH LOGIN PASSWORD 'client2_secure_pass';
CREATE DATABASE client2_odoo OWNER client2;
REVOKE ALL ON DATABASE client2_odoo FROM PUBLIC;Each tenant's Odoo connects with its own credentials and can only access its own database. Even if a SQL injection vulnerability existed, it could not reach another tenant's data.
Connection pooling with PgBouncer
With many tenants hitting one PostgreSQL server, connection pooling becomes necessary. Odoo opens a connection per worker per database — 10 tenants with 4 workers each means 40 persistent connections.
[databases]
client1_odoo = host=127.0.0.1 port=5432 dbname=client1_odoo
client2_odoo = host=127.0.0.1 port=5432 dbname=client2_odoo
client3_odoo = host=127.0.0.1 port=5432 dbname=client3_odoo
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = session
default_pool_size = 20
max_client_conn = 200Point each tenant's odoo.conf at PgBouncer (port 6432) instead of PostgreSQL directly.
Backup strategies per database
#!/bin/bash
# backup-all-tenants.sh — run via cron daily
BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# List of tenant databases
TENANTS="client1_odoo client2_odoo client3_odoo"
for DB in $TENANTS; do
echo "Backing up $DB..."
pg_dump -U postgres -Fc "$DB" > "$BACKUP_DIR/${DB}.dump"
# Optional: encrypt per-tenant backups
gpg --encrypt --recipient "$DB@example.com" "$BACKUP_DIR/${DB}.dump"
rm "$BACKUP_DIR/${DB}.dump"
done
# Rotate: keep 30 days
find /backups -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;Resource Isolation and Limits
Docker resource constraints per tenant
In production, always set resource limits. Without them, one tenant's heavy payroll calculation can OOM-kill another tenant's container.
deploy:
resources:
limits:
memory: 2G # Hard cap — container is killed if exceeded
cpus: "1.0" # Limit to 1 CPU core
reservations:
memory: 512M # Guaranteed minimum
cpus: "0.25" # Guaranteed minimumSizing guide
As a starting point for Odoo tenants:
| Tenant size | Odoo workers | Memory limit | CPU limit | PostgreSQL memory |
|---|---|---|---|---|
| Small (1-10 users) | 2 | 1.5 GB | 0.5 CPU | 512 MB |
| Medium (10-50 users) | 4 | 3 GB | 1.0 CPU | 1 GB |
| Large (50-200 users) | 8 | 6 GB | 2.0 CPU | 2 GB |
| Enterprise (200+ users) | 12+ | 10 GB+ | 4.0 CPU | 4 GB+ |
These are minimums. Heavily customized instances with complex reports need more. The OEC.sh Server Calculator can estimate requirements based on user count and module selection.
PostgreSQL connection limits
In postgresql.conf, cap connections per tenant to prevent one tenant from exhausting the connection pool:
# Global PostgreSQL limits
max_connections = 200Combined with PgBouncer's per-database pool limits, this prevents any single tenant from starving others of connections.
Monitoring with Prometheus and Grafana
For multi-tenant monitoring, export per-container metrics:
# Add cAdvisor to your monitoring stack
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:rocAdvisor exposes per-container CPU, memory, network, and disk metrics. Grafana dashboards can show per-tenant resource usage, making it easy to identify tenants that need more resources — or tenants whose workload has outgrown the shared server.
Scaling Multi-Tenant Odoo
When to split to multiple servers
A single 8-core, 32 GB server comfortably runs 10-15 small tenants or 5-8 medium tenants. When you hit these limits:
- 1Separate the database server. Move PostgreSQL to a dedicated machine (or managed service like RDS/Cloud SQL). This is the single biggest scaling win — Odoo and PostgreSQL competing for the same CPU is the most common bottleneck.
- 2Add compute nodes. Run Odoo containers on multiple servers, all pointing at the same database server. Use Traefik or a load balancer to route tenants to the right compute node.
- 3Shared storage for filestore. When Odoo runs on multiple compute nodes, the filestore (uploaded documents, images) must be shared. Options:
- •NFS mount across compute nodes
- •S3-compatible object storage (MinIO or cloud S3)
- •Odoo's S3 filestore addon (available for Enterprise)
Architecture at scale
┌─────────────┐
│ Load Balancer│
│ (Traefik) │
└──┴───────┴──┘
│ │
┌──────────┘ └──────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Compute Node 1│ │ Compute Node 2│
│ client1-odoo │ │ client3-odoo │
│ client2-odoo │ │ client4-odoo │
└───────┴───────┘ └───────┴───────┘
│ │
└────────┴───────────────┘
▼
┌───────────────┐
│ Database Server│
│ PostgreSQL + │
│ PgBouncer │
└───────────────┘
│
┌───────────────┐
│ Shared Storage │
│ (NFS / S3) │
└───────────────┘Security Considerations
Multi-tenant hosting means you are responsible for preventing data leakage between clients. This is not optional.
Network isolation with Docker networks
Every tenant must be on its own Docker network. The only shared network is the proxy network that connects to Traefik:
networks:
client1-internal:
driver: bridge
internal: true # No external access
proxy:
external: trueThe internal: true flag means containers on client1-internal cannot reach the internet or any other network. They can only talk to each other (Odoo to its own PostgreSQL).
PostgreSQL isolation
- •Separate database users per tenant (no shared superuser)
- •
REVOKE ALL ON DATABASE ... FROM PUBLICon every tenant database - •If sharing a PostgreSQL instance, disable
CREATE DATABASEfor tenant roles - •Consider row-level security for shared tables in multi-database setups (advanced)
Filesystem isolation
- •Each tenant's filestore is a separate Docker volume
- •Never share filestore volumes between tenants
- •Set proper file permissions — the Odoo container runs as UID 101 (odoo user)
Backup encryption
Encrypt backups per tenant. If a backup leaks, only that tenant's data is exposed:
# Per-tenant encryption key
gpg --encrypt --recipient client1@example.com client1_backup.dumpGDPR and data residency
For EU clients, you may need to guarantee that their data stays in a specific region. With Docker, this is straightforward: deploy that tenant's containers on a server in the required region. With OEC.sh, you select the cloud provider and region per project.
SaaS Platform on Odoo
If you are building a full SaaS product on Odoo — automated signup, billing, tenant lifecycle — this is a significantly larger project. A brief overview of the components:
Automated provisioning
When a customer signs up:
- 1Create a new Docker Compose deployment (template with variables)
- 2Create a new PostgreSQL database with isolated credentials
- 3Initialize Odoo with base modules
- 4Configure Traefik labels for routing
- 5Provision SSL automatically
This can be orchestrated with a simple Python/Bash script for small scale, or Kubernetes operators for large scale.
Billing integration
- •Track per-tenant resource usage (CPU hours, storage, users)
- •Integrate with Stripe or a billing platform
- •Implement usage-based or per-user pricing
- •Handle trial periods, upgrades, downgrades
Tenant lifecycle management
- •Suspend (stop containers, retain data)
- •Reactivate (start containers)
- •Delete (destroy containers, database, backups after retention period)
- •Migrate (move tenant to a different server)
- •Upgrade (change Odoo version per tenant)
Self-service portal
A web application (separate from Odoo) where tenants can:
- •View their instance status and resource usage
- •Download backups
- •Install/uninstall modules
- •Upgrade their plan
- •Manage their custom domain
Building a full SaaS platform is a 3-6 month project depending on your automation level. Most teams start with manual provisioning (a script that takes 5 minutes per tenant) and automate incrementally.
Multi-Tenant with OEC.sh
Everything in this guide — Docker containers, PostgreSQL isolation, Nginx/Traefik routing, SSL, resource limits, backups, monitoring — is infrastructure work that does not directly serve your clients.
OEC.sh handles the infrastructure layer so you can focus on the Odoo deployment itself:
- Each project is a fully isolated environment — separate containers, separate database, separate filestore. The full-isolation architecture described above, managed for you.
- Multi-cloud deployment — place tenants on AWS, Hetzner, DigitalOcean, or any supported provider. Per-tenant region selection for data residency requirements.
- Built-in resource limits — per-project CPU and memory allocation, no resource contention between projects.
- Automatic SSL — wildcard or per-tenant certificates, provisioned automatically.
- No Docker or Nginx configuration — deploy in minutes, not hours.
If you are hosting Odoo for multiple clients and want the isolation of the full-container approach without managing the container orchestration, explore the multi-tenant solution or check pricing.
Skip the infrastructure work
OEC.sh gives you fully isolated Odoo environments per client — separate containers, databases, and resource limits — on any cloud provider. No Docker or Nginx configuration required.
- Free tier available
- No credit card required
- Full tenant isolation
Frequently Asked Questions
How many tenants can I run per server?
Depends on tenant size. A 4-core, 16 GB server handles roughly 8–12 small tenants (under 10 users each) with full isolation. Medium tenants (10–50 users) need about 3 GB RAM each, so a 32 GB server fits 8–10. Use the Server Calculator for specific estimates.
Can tenants run different Odoo versions?
Only with full isolation (separate containers). Multi-company and multi-database both share a single Odoo binary, so all tenants are locked to the same version. With per-tenant containers, one client can run Odoo 17 while another runs Odoo 18.
How do I backup individual tenants?
With multi-database or full isolation, each tenant has its own PostgreSQL database. Run pg_dump per database. With full isolation, you can also snapshot the entire Docker volume. For multi-company (single database), you cannot backup one company without the others — it is one database.
Is multi-company or multi-database better?
Multi-company is simpler and cheaper but has no data isolation. Multi-database gives you real isolation with moderate complexity. If your tenants are external paying clients, multi-database is the minimum. If they are internal departments in the same organization, multi-company is usually sufficient.
How do I handle per-tenant custom modules?
With full isolation, each tenant has their own addons directory — install whatever you want. With multi-database, the module files are shared (same Odoo binary) but you can install different modules per database. With multi-company, all companies see all installed modules.
What is the minimum server for 10 tenants?
For 10 small tenants (under 10 users each) with full isolation: 8 CPU cores, 32 GB RAM, 200 GB SSD. For multi-database (shared Odoo binary): 4 cores, 16 GB RAM, 100 GB SSD. Multi-company needs the least: 2 cores, 8 GB RAM. These are starting points — monitor actual usage and scale accordingly.
Deploy Multi-Tenant Odoo in Minutes
Whether you are hosting 5 clients or 50, OEC.sh gives you fully isolated Odoo environments with automatic SSL, resource limits, and multi-cloud deployment — without managing Docker, Nginx, or PostgreSQL configuration.
Related Resources
Multi-Tenant Solutions
Managed multi-tenant Odoo hosting with full isolation per client.
Server Calculator
Calculate the right server size for your Odoo workload.
Odoo Docker Compose Guide
Complete reference for Docker Compose configurations with Odoo.
OEC.sh Pricing
Plans for single-tenant and multi-tenant Odoo deployments.
OEC.sh Features
All platform features including isolation, monitoring, and scaling.
Odoo Docker Guide
Complete guide to running Odoo in Docker containers.
Docker Compose for Odoo
Production-ready Docker Compose with PostgreSQL.
Nginx Reverse Proxy Config
Production Nginx config with SSL and websocket support.
Odoo Performance Tuning
Optimize Odoo for speed and scalability.
Ubuntu Install Guide
Install Odoo on Ubuntu without Docker.