Skip to content
Odoo Releases

Odoo Read Replicas and PostgreSQL Scaling: What Changes in Odoo 20

2026-07-21 · 13 min read

Last updated: 2026-08-09

Odoo 20PostgreSQLScalingInfrastructure

Last updated: August 9, 2026. A read replica is a second PostgreSQL instance that continuously receives a copy of every write made to a primary database, and serves read-only queries so the primary isn't loaded with report generation, dashboard queries and exports on top of the transactional traffic that actually needs to be fast. Odoo 20 is reported to add native support for routing read-heavy work to replica instances. Here is what that actually means at the database level, how people solve this problem today without it, and when you genuinely need this versus when tuning the primary server is the cheaper, simpler answer.

What a read replica actually is

PostgreSQL has supported streaming replication since version 9.0. The mechanism: the primary server writes every change to a write-ahead log (WAL). A replica connects to the primary, streams that WAL continuously, and replays it locally, keeping its own copy of the database in near-real-time sync. The replica is read-only by default, PostgreSQL rejects write attempts against it (ERROR: cannot execute INSERT in a read-only transaction). Replication lag is normally milliseconds to low seconds under healthy network conditions, but it is not zero, which matters for any workload that reads its own just-written data (more on this below).

Odoo read replica architecture: primary handles writes, replicas serve reports and dashboards via PostgreSQL WAL streaming replication

How people scale Odoo reads today, without waiting for Odoo 20

This is not a new problem and it does not require Odoo 20 to solve. The pattern we use today for clients with heavy reporting load:

  1. Stand up a PostgreSQL streaming replica using pg_basebackup and a replication slot on the primary, on separate hardware or at least a separate PostgreSQL process.
  2. Put a connection proxy in front, most commonly PgBouncer or a purpose-built read/write splitter, so application code doesn't need to know which database to talk to.
  3. Route specific known-heavy operations, custom reporting modules, BI exports, scheduled analytics jobs, to the replica's connection string explicitly, since Odoo's ORM itself has no concept of "this query is read-only, route it elsewhere" built in through 19.0.
  4. Never route anything through the replica that a user might read back within the same request/session as a write they just made. This is the real trap: a user updates a record, then the next page load queries the replica and doesn't see their own change yet because of replication lag. This is why most current Odoo replica setups are scoped to reporting and analytics traffic, not general web traffic.

See our PostgreSQL tuning guide for concrete postgresql.conf values, including wal_level, max_wal_senders and hot_standby, the settings that actually enable this on the primary.

What Odoo 20 reportedly changes

The roadmap item is a native read replica architecture: Odoo routing large reports and dashboard queries to replica instances automatically, rather than that routing being something we build by hand with a proxy and manual query targeting. If accurate, this removes the manual routing layer, step 3 above, and potentially makes the read/write split something Odoo's own ORM understands natively rather than an infrastructure-level workaround. We have not independently verified the exact mechanism (whether it is ORM-level query classification, a session-level flag, or something else), and are not going to guess at implementation details Odoo hasn't published.

When you genuinely need a read replica

  • Report generation or dashboard queries measurably slow down transactional operations for other users during business hours (verify this with pg_stat_activity during the slow window, don't guess).
  • You run scheduled exports or BI jobs against the production database that take minutes and lock resources the transactional workload needs.
  • You have genuinely separated reporting users (a finance team running heavy queries) from transactional users (sales/warehouse staff) and can afford the architectural complexity of routing between them.

When tuning the primary is the cheaper answer

Most Odoo installations that feel slow do not actually need a read replica. Before reaching for one, check: is shared_buffers set to something close to 25% of RAM, or still at the 128MB default? Are there missing indexes on frequently filtered fields (check pg_stat_user_tables for high sequential scan counts on large tables)? Are workers and db_maxconn correctly sized for the server (see our workers formula post)? A read replica adds a second server to patch, monitor and pay for, plus the operational complexity of managing replication lag and failover. In our experience across 70+ deployments, the majority of "we need to scale reads" requests are solved by correct primary tuning and are never revisited once fixed. Reach for a replica when tuning has demonstrably hit its ceiling, not before.

What operating a replica actually costs you day to day

Before committing to a replica setup, it's worth being clear-eyed about the ongoing operational cost, not just the initial setup effort. A replica is a second PostgreSQL server that needs its own monitoring (is it actually keeping up, or silently falling behind), its own patching schedule kept in sync with the primary's PostgreSQL version, and a defined failover story, what happens to the reporting workload if the replica goes down, and separately, what your plan is if the primary fails and you need to promote the replica. None of this is exotic, PostgreSQL's replication tooling is mature and well documented, but it is real, ongoing work that a single-server setup doesn't have. Budget for it honestly rather than treating replica setup as a one-time task.

Monitoring replication lag in practice

If you do run a replica, watching lag is not optional, it's the one metric that tells you whether the whole setup is actually doing its job:

# On the primary, check how far behind each replica is
SELECT client_addr, state,
  pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes
FROM pg_stat_replication;

# On the replica itself, a simpler time-based check
SELECT now() - pg_last_xact_replay_timestamp() AS lag_interval;

Alert on lag exceeding a threshold appropriate to your workload, a few seconds is usually fine for reporting traffic, but a replica that falls minutes behind under load is a sign something's wrong (network saturation, a replica that's underpowered for the WAL volume it needs to replay, or a long-running query on the replica blocking WAL apply) and needs investigating before it becomes a bigger problem.

A note on connection pooling with a replica in the mix

If you're already running PgBouncer for connection pooling on the primary (a good idea regardless of replicas, see our workers formula post on why db_maxconn sizing matters), adding a replica typically means a second PgBouncer pool pointed at the replica's connection string, rather than trying to route both primary and replica traffic through one pool. Keeping the pools separate makes it much easier to reason about which traffic goes where, and avoids a misconfigured routing rule accidentally sending a write to a read-only replica and failing at the worst possible moment.

Failover: what happens when the primary dies, not just the replica

It's worth being precise about a common misconception: a read replica, on its own, is not a high-availability solution for the primary. If your primary PostgreSQL instance fails, the replica doesn't automatically become the new primary and start accepting writes, that requires either a manual promotion (pg_ctl promote or the equivalent for your PostgreSQL version) or a separate automated failover tool (Patroni and repmgr are the two most established options in the PostgreSQL ecosystem). If your actual goal is protecting against primary failure rather than offloading read traffic, that's a different, more involved project than the reporting-offload pattern described in this post, and it's worth being clear internally about which problem you're actually solving before building either one, since the tooling and operational discipline required differ meaningfully.

FAQ

Does a read replica reduce load on my primary database?

Yes, for the specific queries you route to it. It does not reduce write load, which stays entirely on the primary.

Is replication lag a problem for Odoo?

Only if you route request paths to the replica where a user might read data they just wrote. For pure reporting and analytics traffic, sub-second lag is rarely noticeable.

Do I need Odoo 20 to use a read replica?

No. PostgreSQL streaming replication plus a connection proxy works on Odoo 19 and earlier today, we deploy this pattern already. Odoo 20 is reported to make the routing native rather than infrastructure-managed.

How many replicas do I actually need?

Almost always one is enough. Additional replicas add redundancy for the reporting workload, not more scale, since read-only query volume for most Odoo installs doesn't approach the point where a single replica is saturated.

Sources: PostgreSQL streaming replication documentation, Odoo Partner Days roadmap presentation (April 2, 2026).

Talal Yousaf, Senior DevOps Engineer at DevFusion Tech. 70+ Odoo deployments across Community and Enterprise, versions 13 through 19. More about DevFusion · LinkedIn

Published 2026-07-21 · updated 2026-08-09

ShareLinkedInXWhatsApp

Hit the same wall in production?

Tell us what you're running and where it breaks. A 30 minute call, no pitch deck, and a straight answer on whether we can help.