Always excited to take on new projects and collaborate with innovative ideas.

Phone

+968 97716144

Email

contact@aljulanda.info

Website

https://aljulanda.info

Address

Sultanate of Oman - Nizwa

Technical Guides

Odoo Running Slow? Diagnose Workers, RAM and PostgreSQL Before You Blame the ERP

Odoo feels sluggish? Before you migrate servers, check your worker mode, RAM sizing, memory/time limits, and PostgreSQL autovacuum health using Odoo's own official numbers.

Odoo Running Slow? Diagnose Workers, RAM and PostgreSQL Before You Blame the ERP
"Odoo is slow" is one of those complaints that gets thrown at the whole system when the actual fault sits in three or four specific, checkable places. I hear it constantly from businesses running Odoo on their own server — electronics retailers watching the POS lag at checkout, food distributors staring at a stock module that takes ten seconds to load a picking list, agriculture operations timing out on reports during month-end. Nobody blames the config file. Everybody blames "the ERP." That's usually wrong.

The symptom vs. the cause

Odoo itself, as a piece of software, is not the bottleneck in the vast majority of on-premise slowdowns. The bottleneck is almost always one of three things: the server is running in a mode that can't handle concurrent users, the RAM allocated per worker process doesn't match reality, or PostgreSQL has been quietly bloating for months because nobody looked at autovacuum. None of these show up as an error message. They just show up as "it's slow today," which is exactly why they get misdiagnosed as an Odoo bug and "fixed" by throwing a bigger VM at the problem instead of fixing the configuration.

Step 1 — Are you even running in multi-processing mode?

Open your Odoo config file (usually /etc/odoo/odoo.conf) and look for the workers parameter. If it's absent or set to 0, you are running in threaded mode — a single process handling every request sequentially. That's fine for a developer laptop. It is not fine for five or ten people hitting the same server at once, because one long-running report or a heavy import will freeze everything else behind it. If your production instance has more than a handful of concurrent users and workers is still 0, you've likely already found your main problem before touching anything else.

Step 2 — Size your workers against real usage

Multi-processing mode splits incoming requests across separate worker processes instead of one thread trying to do everything. Odoo's own reference example for a production deployment lands on 8 HTTP workers plus 1 dedicated cron worker — that's the number worth using as your anchor point, not a guess pulled from a forum post. Watch your CPU load while your busiest users are active (top, htop, or your monitoring tool of choice) and compare it against how many workers are actually configured. If your CPU is sitting idle while users complain about lag, you're under-provisioned on workers, not on hardware. If CPU is pegged at 100% with only two or three workers configured, adding more workers on the same undersized box will just create swapping, not speed.

Step 3 — Calculate RAM per worker properly

This is the step almost everyone skips, and it's the one that causes OOM (out-of-memory) kills — the server silently murdering a worker process mid-request, which the end user just experiences as "the page didn't load." Odoo's official sizing approach is a weighted formula:

RAM = workers × ((0.8 × light-request RAM) + (0.2 × heavy-request RAM))

In other words: most requests are light (list views, simple form loads), but a fifth of your traffic is heavy (reports, imports, large searches), and your RAM budget needs to cover both, not just the average. In Odoo's own worked example, this formula lands at roughly 3 GB for Odoo's own processes on an 8-worker setup — and that 3 GB figure is what then gets used to size the per-worker memory limits below. If your server has 4 GB total RAM and you're also running PostgreSQL, your mail relay, and maybe a Samba share on the same box, you're already underwater before a single user logs in.

Step 4 — Set memory and time limits, don't leave them at defaults

Four parameters control how aggressively Odoo protects itself from a single runaway process eating the whole server. Odoo's official reference configuration sets them like this:

  • limit_memory_soft = 629145600 (≈600 MB) — once a worker crosses this during a request, Odoo lets that request finish, then kills and respawns the worker before it takes the next one.
  • limit_memory_hard = 1677721600 (≈1.6 GB) — cross this and the worker is killed immediately, no grace period, to protect the rest of the system.
  • limit_time_cpu = 600 — max CPU seconds a request gets before it's killed.
  • limit_time_real = 1200 — max wall-clock seconds before a request is killed, covering time spent waiting on I/O too.
  • limit_request = 8192 — number of requests a worker handles before it recycles itself, which helps against slow memory creep.

If your config file still has the installer's defaults and you've never touched these, you're running blind. Undersized limits kill legitimate heavy reports mid-run. Oversized limits (or none at all) let one bad request drag your RAM usage up until the kernel's OOM killer picks a worker to sacrifice — often not even the one causing the problem.

Step 5 — Don't forget the cron worker, LiveChat, and proxy mode

The cron worker (max_cron_threads, set to 1 in the official example) handles scheduled actions — invoicing runs, stock valuation, automated emails. If it's missing or set to 0, your background jobs silently pile up and eventually start competing with live user traffic for resources.

Separately, if you use Odoo's LiveChat, multi-processing mode automatically starts a dedicated LiveChat worker that listens on the --gevent-port. Here's the catch: by default, HTTP requests keep hitting your normal HTTP workers instead of that LiveChat worker. You need a reverse proxy (Nginx is the usual choice) in front of Odoo that specifically redirects any path starting with /websocket/ to the LiveChat worker's port. Skip this and LiveChat either doesn't work properly or quietly degrades your normal worker pool's performance.

While you're touching the proxy config, make sure Odoo is started with --proxy-mode enabled. Without it, Odoo trusts the proxy's own headers instead of the real client's hostname, scheme, and IP — which breaks logging, security checks, and anything relying on knowing who's actually connecting.

Step 6 — Check PostgreSQL's autovacuum health

You can get workers and RAM perfectly right and still have a slow system because the database itself is bloated. Every update and delete in PostgreSQL leaves behind dead tuples — old row versions that aren't cleaned up automatically in real time. That's autovacuum's job, and it's controlled by parameters in postgresql.conf. The autovacuum launcher daemon runs by default ("on"), but it depends on track_counts being enabled — if that's off, autovacuum effectively does nothing even though it looks "on" in the config.

Two defaults matter a lot as your database grows: autovacuum_vacuum_threshold defaults to just 50 tuples, and autovacuum_vacuum_insert_threshold (the trigger for insert-heavy tables) defaults to 1000 tuples. These thresholds were sane for small databases. Most self-hosted Odoo instances never revisit them as transaction volume grows into the millions of rows — stock moves, account move lines, mail messages — and autovacuum starts falling behind, leaving tables and indexes bloated. Every query against a bloated table scans more dead weight than it needs to, and no amount of worker or RAM tuning fixes that.

Run SELECT relname, n_dead_tup, n_live_tup FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 15; and see what comes back. A dead-tuple count that dwarfs live tuples on your biggest tables is your answer.

Your diagnostic checklist for this week

  • Confirm workers is set and greater than 0 — no multi-processing means no real concurrency.
  • Compare configured worker count against actual CPU load during peak hours.
  • Recalculate RAM using the 0.8/0.2 light/heavy-request formula against total available memory.
  • Verify limit_memory_soft, limit_memory_hard, limit_time_cpu, and limit_time_real are explicitly set, not left blank.
  • Confirm max_cron_threads is set and your proxy correctly routes /websocket/ traffic if LiveChat is in use.
  • Check track_counts is on and query pg_stat_user_tables for dead tuple counts on your largest tables.

Go through that list before you touch your budget for new hardware. Most of the time, the fix is a config file edit and a database vacuum, not a bigger server.

References

Image: dylanroscover — BY-SA (via Openverse)

Odoo ERP, Servers & Hosting, IT Operations
7 min read
Jul 06, 2026
By Aljulanda Alhadidi
Share

Leave a comment

Your email address will not be published. Required fields are marked *

Related posts

Jul 07, 2026 • 8 min read
Why Odoo Invoices Land in Spam: Fixing SPF, DKIM and DMARC for Gmail/Yahoo

Gmail and Yahoo now reject unauthenticated mail at the SMTP level. Her...

Apr 08, 2026 • 3 min read
Active Directory: Complete Practical Guide for Domain Design, Group Policy, and Samba Integration

Comprehensive practical guide to planning and implementing Microsoft A...