When Does Odoo Start Struggling?
A default Odoo installation with 2 workers and 4 GB RAM handles roughly 10–20 concurrent active users before response times degrade. "100+ users" means 100 users who might all be active in a busy workday — not all simultaneously making requests, but generating significant concurrent load. Scaling to this level requires intentional configuration at every layer.
Hardware Sizing Baseline
The Odoo documentation provides a formula. For 100 concurrent users:
- CPU: 8+ cores (each worker uses one core at peak)
- RAM: 16–32 GB (workers + PostgreSQL shared buffers + OS overhead)
- Storage: NVMe SSD for PostgreSQL data directory
- Network: 1 Gbps is sufficient; bandwidth is rarely the bottleneck
See Odoo Server Requirements for detailed sizing tables.
Worker Tuning
The Odoo worker formula is:
workers = (CPU_cores * 2) + 1
# Example: 8 cores -> 17 workers
# In odoo.conf:
workers = 17
max_cron_threads = 2
limit_memory_soft = 2684354560 # 2.5 GB
limit_memory_hard = 3221225472 # 3 GB
limit_time_cpu = 600
limit_time_real = 1200
The memory limits cause Odoo to recycle workers that have grown too large (due to memory leaks in third-party modules), preventing gradual memory exhaustion. See Configure Odoo Workers for the full reference.
PostgreSQL Optimisation
PostgreSQL is almost always the bottleneck at scale. Key settings for a 32 GB RAM server:
# /etc/postgresql/16/main/postgresql.conf
shared_buffers = 8GB # 25% of RAM
effective_cache_size = 24GB # 75% of RAM
work_mem = 64MB # per sort/hash operation
maintenance_work_mem = 1GB # for VACUUM, index creation
max_connections = 200 # size for pgBouncer pool
wal_buffers = 64MB
checkpoint_completion_target = 0.9
random_page_cost = 1.1 # for SSD
effective_io_concurrency = 200 # for SSD
Restart PostgreSQL after these changes. See Optimize PostgreSQL for Odoo for the full guide.
Connection Pooling with pgBouncer
Each Odoo worker opens its own PostgreSQL connection. At 17 workers, that is 17 connections — manageable. But if you later run multiple Odoo instances, connections multiply. pgBouncer pools connections efficiently:
# /etc/pgbouncer/pgbouncer.ini
[databases]
mydb = host=127.0.0.1 dbname=mydb
[pgbouncer]
pool_mode = transaction
max_client_conn = 500
default_pool_size = 20
listen_port = 6432
Point db_port = 6432 in odoo.conf to use pgBouncer. Use transaction pool mode, not session mode, for Odoo.
CDN for Static Assets
Static assets (CSS, JS, images, attachments) can be offloaded to a CDN, reducing load on Odoo workers:
# In odoo.conf:
cdn_activated = True
cdn_url = https://cdn.example.com
cdn_filters = ^/web/(css|js)/|^/website/static/|^/base/static/
Or use nginx to serve the /web/assets/ path with long cache headers directly, without proxying to Odoo:
location ~* ^/web/assets/ {
proxy_pass http://127.0.0.1:8069;
proxy_cache odoo_cache;
proxy_cache_valid 200 1d;
add_header Cache-Control "public, max-age=86400";
expires 1d;
}
nginx Performance Tuning
# /etc/nginx/nginx.conf
worker_processes auto;
worker_connections 4096;
keepalive_timeout 65;
gzip on;
gzip_types text/css application/javascript application/json;
gzip_min_length 1024;
Monitoring Under Load
Before going live with 100+ users, load test with a tool like Locust or k6. Watch:
- PostgreSQL
pg_stat_activity— connections should not hitmax_connections - Odoo log for worker timeout warnings
- Server RAM usage — leave 20% headroom
- p95 response time — target under 500 ms for common operations
See Monitor Odoo Performance for a complete monitoring setup.
How DeployMonkey Scales With You
DeployMonkey's Professional plan ($29/month) is right-sized for teams of 20–50, and the Enterprise plan ($150/month) covers 100+ users with dedicated resources, pgBouncer pre-configured, and auto-scaling workers. You can upgrade without any downtime or migration work — just click upgrade in the control panel.
Start free at deploymonkey.app.
Frequently Asked Questions
Does the number of installed modules affect performance?
Yes — each installed module adds to startup time and can add database queries to common operations. Audit your modules and uninstall anything not actively used.
Should I use Odoo multi-processing or multi-threading?
Multi-processing (workers > 0) is required for production. Multi-threading (workers = 0) is only for development — it cannot take advantage of multiple CPU cores.
What is the maximum number of workers I should run?
A practical ceiling is (CPU_cores * 2) + 1. Beyond this, workers compete for CPU time and performance degrades. Horizontal scaling (multiple servers) is more effective at very high load.
How much RAM does each Odoo worker use?
A fresh worker starts at ~150 MB and grows to 300–600 MB under normal use. Set limit_memory_soft = 2.5 GB to recycle workers before they grow larger. Plan for ~500 MB per worker as a sizing baseline.