Key takeaways
- PostgreSQL's defaults are deliberately conservative. Sizing memory to the server is the first and easiest win.
- Measure before you tune: turn on query statistics and slow-query logging, then fix the queries that cost the most time.
- Keep Odoo's connection pool and PostgreSQL's connection limit in step, especially with several Odoo instances on one server.
- Change one setting at a time, write down why, and check the effect.
Why PostgreSQL tuning matters for Odoo
Odoo keeps everything in PostgreSQL: records, settings, translations, attachments' metadata and the queue of scheduled actions. Python code builds the screens, but it waits on the database for almost every click. When Odoo feels slow, the database is often where the time goes, either because PostgreSQL has too little memory to work with, or because a few heavy queries run far more often than anyone realised.
A fresh PostgreSQL installation is set up to start on almost any machine, so its defaults are small. They are safe, but they leave most of a modern server unused. Tuning is about telling PostgreSQL what hardware it really has, then removing the specific bottlenecks your Odoo database shows.
Odoo PostgreSQL memory settings
Four settings decide how PostgreSQL uses memory: shared_buffers,
effective_cache_size, work_mem and
maintenance_work_mem. On most Odoo servers PostgreSQL shares the machine
with the Odoo workers, so it should take a smaller share than it would on a dedicated
database server. Our
Odoo server sizing guide gives the
formulas and a ready-made table from 2 to 16 vCPUs, so we won't repeat them here.
One warning about work_mem: it applies per sort or hash operation, not
per connection. A complex report can use several at once, in several connections at
once. Raise it carefully, and watch memory after you do.
Settings for SSD storage
Almost every cloud server today runs on SSDs, but some PostgreSQL defaults still assume spinning disks.
random_page_cost
This tells the query planner how expensive it is to read a page from a random place on disk. The default is 4.0. PostgreSQL's documentation notes that solid-state drives may be better modelled with a lower value, and around 1.1 is a common choice on SSD. A lower value makes the planner use indexes more readily, which suits Odoo's many lookups by ID and foreign key.
effective_io_concurrency
This tells PostgreSQL how many reads the storage can handle in parallel. SSDs handle far more than a single disk, so a value in the low hundreds is common.
Checkpoints and WAL
PostgreSQL writes changes to its write-ahead log (WAL) and flushes them to the data
files at checkpoints. Too-frequent checkpoints cause bursts of disk writes, which you
may notice during large imports or module updates. Raising
max_wal_size to a few GB and spreading checkpoints out with
checkpoint_completion_target smooths this out. If the PostgreSQL log
warns that checkpoints are occurring too frequently, that is the setting to look at.
Odoo database connections and db_maxconn
Each Odoo process keeps its own pool of database connections, limited by the
db_maxconn option in the Odoo configuration. PostgreSQL has its own
limit, max_connections, which is typically 100 by default. If the Odoo
workers, cron processes and any other tools together try to open more connections than
PostgreSQL allows, users see errors like "too many clients already".
A few rules keep this under control:
-
Add it up. Count the Odoo processes across every instance on the
server, multiply by their pool size, and keep the total under
max_connectionswith some room for backups and maintenance. -
Lower
db_maxconnbefore raisingmax_connections. Each PostgreSQL connection uses memory, so a very high limit can cause trouble of its own. -
Watch for idle transactions. A connection left "idle in
transaction" holds locks and blocks cleanup. Setting
idle_in_transaction_session_timeoutends them after a while.
Find slow Odoo queries first
Settings help everywhere a little. Fixing the few slowest queries often helps a lot more. Two tools show where the time goes.
pg_stat_statements
This extension, which ships with PostgreSQL, records every query shape with how often it ran and how much time it took in total. Sort by total time to find the queries worth fixing. A query that takes 50 ms but runs 100,000 times a day matters more than one that takes 5 seconds once a week.
Slow-query logging
The log_min_duration_statement setting logs every statement that runs
longer than the time you give it. It is off by default. Setting it to, for example,
one second shows the slow queries along with the time they ran, so you can match them
to what users were doing.
What to do with a slow query
-
Run
EXPLAIN ANALYZEon it, on a staging copy, to see whether it scans a whole table. -
Add an index where a custom module filters or sorts on a field that
has none. In Odoo you do this in the module, with
index=Trueon the field, so it survives updates. - Fix the code. Many slow queries come from custom modules that read records one at a time in a loop, or from computed fields that are stored and recomputed too often.
Test index changes on a copy of production first. Our article on Odoo staging environments explains how to make one safely.
Autovacuum and bloat in Odoo databases
When Odoo updates or deletes a row, PostgreSQL keeps the old version until a vacuum cleans it up. Autovacuum does this in the background. On busy tables, such as mail messages, stock moves and queue tables, it can fall behind. Tables then grow larger than their data (bloat), and queries slow down.
- Leave autovacuum on. Turning it off is one of the most harmful changes you can make.
-
Make it start sooner on big tables. By default it waits until a
fixed share of a table has changed, which on a table with millions of rows means a
lot of dead rows. Lowering
autovacuum_vacuum_scale_factor, for the whole server or for specific large tables, makes it run earlier and more often. -
Keep statistics fresh. The planner relies on statistics that
autovacuum's analyze step updates. After a large import, run
ANALYZEyourself.
How to change PostgreSQL settings safely
- Take a backup, or at least note the current values.
- Change one setting, or one related group, at a time.
-
Check whether the setting needs a reload or a full restart. Settings such as
shared_buffersandmax_connectionsonly change on restart, which briefly interrupts every Odoo instance on the server. - Make sure PostgreSQL starts again, then watch response times and memory.
- Write down what you changed and why.
The full list of parameters, with what each one does, is in the PostgreSQL server configuration documentation.
PostgreSQL tuning in CloudPilot
On servers in your own cloud account or connected over SSH, CloudPilot shows the PostgreSQL settings on the server page:
-
One-click optimize. CloudPilot sizes memory and parallel workers to
the server, applies SSD-friendly values for
random_page_costandeffective_io_concurrency, sets checkpoint and WAL values, and turns onpg_stat_statementsfor query statistics. -
Edit individual parameters, including connection limits,
autovacuum, timeouts and
log_min_duration_statement. Parameters that need a restart are marked. - A safety net. If PostgreSQL fails to start after a change, CloudPilot restores the previous settings and starts it again.
Running your own servers is part of the Pro and Business plans; on Starter, CloudPilot runs the server and its database for you. See pricing for each plan, and managed Odoo hosting for what CloudPilot looks after on every plan.
PostgreSQL tuning for Odoo: questions
What are the most important PostgreSQL settings for Odoo?
Start with memory: shared_buffers, effective_cache_size, work_mem and maintenance_work_mem, sized for a server that also runs Odoo. On SSD storage, also lower random_page_cost. Then use query statistics to find and fix the slowest queries.
Why does Odoo say "too many clients already"?
The Odoo processes are trying to open more database connections than PostgreSQL's max_connections allows. Add up the pool sizes (db_maxconn) of every Odoo process on the server, and lower them or raise max_connections carefully.
Should I turn off autovacuum to make Odoo faster?
No. Without autovacuum, dead rows pile up, tables bloat and queries get slower over time. Make it run earlier on large, busy tables instead.