One store was fast.
Five stores were acceptable.
Then more locations came online, product data grew, custom POS logic accumulated, and a simple product search occasionally started feeling much slower than it should.
The first assumption was predictable:
"The server needs more resources."
Sometimes it does.
But in an Odoo POS multi-store deployment, latency can come from several different layers:
Browser
↓
Network
↓
Reverse Proxy
↓
Odoo
↓
PostgreSQL
↓
Custom Modules
↓
External Services
Adding CPU before identifying which layer is slow can improve nothing.
A better approach is to measure the transaction from the cashier's click all the way to the database—and work inward from there.
The First Question: What Is Actually Slow?
"POS is slow" is not a useful bug report.
We first split the problem into specific actions:
Opening a POS session
Searching products
Adding a product
Selecting a customer
Changing quantity
Processing payment
Printing receipt
Validating an order
Synchronizing data
Closing the session
This matters because each action stresses a different part of the system.
For example:
- Slow initial loading may point toward excessive POS data.
- Slow product search may be browser-side.
- Slow order validation may involve backend queries or custom business logic.
- Slow payment completion may involve an external terminal or API.
- Problems affecting only one store may indicate local connectivity rather than Odoo itself.
Our first improvement was therefore simple:
Stop measuring "Odoo speed." Measure individual POS operations.
1. Compare Stores Before Touching the Code
In a multi-store environment, different locations give you a built-in comparison test.
Suppose we record:
Store A → Fast
Store B → Fast
Store C → Slow
Store D → Fast
Store E → Slow
That immediately changes the investigation.
If every store is slow, look more closely at shared infrastructure:
Odoo workers
PostgreSQL
shared custom modules
server resources
common integrations
If only one location is slow, investigate:
Internet connection
Wi-Fi quality
browser/device
local hardware
payment terminal
IoT equipment
store-specific POS configuration
This comparison prevents a local network issue from becoming an unnecessary backend optimization project.
2. Inspect the Browser Before Blaming PostgreSQL
Odoo POS runs in the browser, and current Odoo documentation describes the POS interface as a specialized single-page application.
That makes browser profiling extremely useful.
Open DevTools and inspect:
Network
Performance
Memory
Console
Start with the Network tab.
For each slow operation, ask:
Was an HTTP request made?
How long did it take?
Was most of the delay waiting for the server?
Was a large payload downloaded?
Were several requests triggered unnecessarily?
A simple mental model is:
User click
↓
JavaScript processing
↓
RPC/request
↓
Odoo processing
↓
Database
↓
Response
↓
Browser rendering
If the server responds quickly but the UI remains frozen, PostgreSQL is probably not the first place to investigate.
3. Measure POS Startup Separately
One of the most noticeable problems in a large Odoo POS multi-store setup is session startup.
As the deployment grows, so can:
- products,
- variants,
- customers,
- pricelists,
- taxes,
- categories,
- custom fields,
- and data introduced by custom POS modules.
A useful debugging question is:
Does every POS terminal really need every record being loaded into its working context?
For example, imagine a deployment with stores serving very different product ranges.
Store A → Electronics
Store B → Furniture
Store C → Accessories
If customizations force every location to process data it never uses, startup and browser work can increase unnecessarily.
In larger deployments, the design of the underlying Odoo POS solution also matters, particularly when custom functionality, inventory synchronization, integrations, and multiple locations are involved.
Before optimizing code, inspect what your custom modules add to the POS payload.
4. Profile Odoo Instead of Guessing
Once browser measurements indicate backend delay, move to Odoo.
Odoo provides an integrated profiler capable of recording SQL activity and execution traces, making it much more useful than trying to infer bottlenecks from CPU usage alone.
Suppose validating an order is slow.
Instead of saying:
Order validation takes too long.
profile the operation and ask:
How many queries run?
Which methods consume the most time?
Is the same query repeated?
Is custom code performing work per order line?
Are expensive computed fields being triggered?
The goal is to turn:
POS feels slow
into something actionable:
80% of this request is spent inside one custom method.
That is a bug you can work with.
5. Watch for N+1 Queries in Custom Modules
Customizations were one of the first places worth checking.
Consider this simplified pattern:
for line in order.lines:
product = env["product.product"].browse(line.product_id.id)
rule = env["custom.rule"].search([
("product_id", "=", product.id)
])
That looks harmless with a three-line order.
With larger transactions and many concurrent POS sessions, repeated searches can become expensive.
A better design may retrieve the required records in batches.
Conceptually:
product_ids = order.lines.mapped("product_id").ids
rules = env["custom.rule"].search([
("product_id", "in", product_ids)
])
The exact implementation depends on the business logic, but the principle remains:
Avoid querying inside loops when the same information can be fetched efficiently as a set.
Odoo's performance documentation specifically recommends batching operations and avoiding algorithmic patterns that create excessive queries.
6. Check What Custom POS JavaScript Is Doing
Backend optimization alone is not enough.
Modern Odoo's frontend framework uses Owl components, and POS uses Odoo's JavaScript application framework.
Custom POS modules may introduce frontend issues such as:
Repeated filtering of large arrays
Unnecessary component updates
Large synchronous loops
Repeated RPC calls
Expensive getters
Duplicate event listeners
Large custom datasets
Consider:
getAvailableProducts() {
return this.products.filter(
product => this.checkComplexRule(product)
);
}
If that calculation runs repeatedly during rendering against thousands of products, the browser may become the bottleneck even when the server is healthy.
The important distinction is:
Slow RPC response ≠ Slow rendering
Measure both.
7. Separate Network Latency from Application Latency
A multi-store deployment often means geographically separated locations.
The Odoo server may live in one region while POS terminals operate hundreds or thousands of kilometers away.
Measure network behavior independently.
For example:
Store → Reverse proxy
Reverse proxy → Odoo
Odoo → PostgreSQL
If one store consistently has higher request latency while others using the same database are fast, application code becomes less suspicious.
This also means testing over the same type of connection cashiers actually use.
A developer testing from a fast office connection may never reproduce a store running over congested Wi-Fi.
8. Don't Ignore the POS Device
Sometimes the backend is fast.
The network is fast.
And the terminal is not.
In stores, POS devices can stay operational for years.
Common symptoms include:
High browser memory usage
Old browser versions
Low available RAM
CPU-heavy extensions
Multiple background applications
Long-running browser sessions
Compare the same POS configuration on:
Device A
Device B
and then compare:
Same device + different store/network
Simple A/B testing can quickly separate hardware problems from application problems.
9. Test Integrations Individually
Payment terminals, loyalty systems, inventory services, shipping APIs, custom pricing engines, and other integrations can all become part of the checkout path.
Odoo supports integrations with payment terminals as part of POS workflows.
If an external integration is involved, measure it independently:
POS action
↓
Odoo
↓
External service
↓
Response
↓
Odoo
↓
POS
A slow external response can make the user experience look like an Odoo performance issue.
For custom integrations, useful logging might capture:
integration_start
integration_end
elapsed_time
result
without exposing sensitive payment or customer information.
Now you can determine whether the delay is:
Odoo → 120 ms
External service → 2.4 s
instead of optimizing the wrong component.
10. Test Under Realistic Concurrency
A POS deployment that performs well with one developer clicking around is not necessarily ready for multiple stores.
The real scenario might look like:
Store 1 → 6 terminals
Store 2 → 8 terminals
Store 3 → 4 terminals
Store 4 → 10 terminals
Store 5 → 5 terminals
During peak periods, those terminals can validate transactions at roughly the same time while other Odoo users continue using Inventory, Sales, Accounting, or eCommerce.
Performance testing should therefore represent real behavior rather than only record count.
Test scenarios such as:
POS login
Product search
Customer lookup
Add 10 products
Apply pricing
Validate payment
Create order
Synchronize stock
under expected concurrency.
The Debugging Workflow That Worked Better
After troubleshooting several layers separately, the process became much clearer:
Reproduce the slow action
↓
Identify affected stores
↓
Measure browser timing
↓
Measure network timing
↓
Profile Odoo
↓
Inspect SQL activity
↓
Inspect custom modules
↓
Measure integrations
↓
Test the fix
↓
Compare before vs. after
The most important part is the final step.
Without a baseline, statements such as:
"It feels faster now."
are difficult to trust.
Record measurable values before making changes.
For example:
Before After
--------------------------------------
POS startup X ms Y ms
Product search X ms Y ms
Order validation X ms Y ms
Payment workflow X ms Y ms
Use actual measurements from your environment rather than arbitrary performance targets.
A Useful Diagnostic Matrix
When investigating an Odoo POS multi-store latency problem, this quick matrix helps narrow the search.
| Symptom | First Area to Check |
|---|---|
| Only one store is slow | Store network/device |
| Every store is slow | Shared backend/database |
| Initial POS load is slow | Loaded datasets/custom POS models |
| UI freezes without slow requests | Frontend JavaScript |
| RPC requests are slow | Odoo/backend profiling |
| Queries dominate request time | PostgreSQL/ORM usage |
| Payment only is slow | Payment integration |
| Performance drops at peak time | Concurrency/resources |
| Custom workflow only is slow | Custom module |
| Old terminals are slower | Browser/device performance |
The table is not a diagnosis.
It simply tells you where to begin measuring.
What Improved the Process Most
The biggest improvement wasn't adding more CPU.
It was making performance observable.
Instead of:
Cashier
↓
"POS is slow"
↓
Developer guesses
we moved toward:
Cashier reports exact action
↓
Browser timing
↓
Network timing
↓
Odoo profile
↓
Query analysis
↓
Measured fix
That made conversations with both developers and store teams much easier.
For production environments where performance problems continue beyond initial debugging, structured Odoo support and performance optimization can also help address server issues, custom-module problems, integrations, and ongoing POS reliability.
Final Takeaway
Latency in an Odoo POS multi-store deployment rarely has one universal cause.
The bottleneck may be:
Too much frontend work
Too much loaded data
A custom module
An inefficient ORM pattern
A slow integration
Network latency
Device limitations
Insufficient capacity
The positive part is that each of those problems is measurable.
Start with the cashier action that feels slow. Compare stores. Inspect the browser. Measure the network. Profile Odoo. Examine database activity and customizations only when the evidence points there.
That approach turns:
"Odoo POS gets slow when we add stores."
into a much better engineering question:
"Which part of this transaction becomes slower as our deployment scales?"
Once you can answer that, optimization becomes much more straightforward.

Top comments (0)