A common Odoo production problem starts innocently: a custom module works correctly with 20 records, but becomes slow when users process thousands of records concurrently. The root cause is rarely Odoo alone. It is usually a combination of ORM usage, PostgreSQL queries, worker sizing, scheduled jobs, custom business logic, and deployment configuration.
This is where Odoo Implementation Services need to be treated as an engineering discipline rather than an installation task. The architecture should be designed around actual transaction patterns, database volume, integrations, and concurrency requirements.
For teams planning a production deployment, Odoo implementation services should therefore include profiling, database design, deployment configuration, testing, and operational monitoring from the beginning.
Context and Setup
The right architecture starts with understanding how Odoo processes requests. A typical production deployment contains an Odoo application layer, PostgreSQL, a reverse proxy, background workers, scheduled actions, filestore storage, and external integrations.
Odoo's current deployment documentation provides a useful sizing reference: its rule of thumb is (# CPU × 2) + 1 workers, while one worker is estimated at approximately six concurrent users. Odoo also notes that worker count alone does not solve slow application logic.
Before implementation, establish these prerequisites:
- Define expected concurrent users and peak transaction volume.
- Identify modules requiring customization.
- Map external APIs and synchronization frequency.
- Estimate PostgreSQL database and filestore growth.
- Separate synchronous user operations from asynchronous jobs.
- Establish response-time and error-rate baselines.
This prevents infrastructure sizing from becoming a guess made after production deployment.
Odoo Implementation Services: A Performance-First Architecture
A production implementation should optimize the application, database, and deployment layers together. Changing only server resources can hide inefficient Python or SQL code rather than fixing it.
Step 1: Profile the Transaction Before Optimising It
The first step is to identify where time is actually being spent.
Odoo provides an integrated profiler that can record SQL queries and execution traces. Its documentation specifically recommends profiling to identify which part of a program is responsible for performance problems.
A practical workflow is:
- Reproduce the slow operation with realistic data.
- Enable SQL and trace profiling.
- Identify repeated queries and expensive methods.
- Check whether the ORM is performing unnecessary record-by-record operations.
- Compare database time with Python execution time.
- Repeat the measurement after every meaningful change.
For example, avoid designing custom logic that repeatedly searches the database inside a loop:
# Why: one batched search avoids issuing a query for every record.
partners = self.env["res.partner"].search([
("email", "in", emails)
])
partner_by_email = {
partner.email: partner
for partner in partners
}
for email in emails:
partner = partner_by_email.get(email)
# Continue processing with the already-loaded record.
The important principle is not simply "write faster Python." It is to reduce unnecessary database round trips.
Step 2: Make PostgreSQL and ORM Work Together
The second step is database-aware module development.
Odoo's performance guidance recommends batch operations, reducing algorithmic complexity, and using indexes where appropriate. It also warns that excessive indexes consume storage and can increase the cost of insert and update operations.
For a custom model, an index can be appropriate when a field is frequently used for filtering:
class Shipment(models.Model):
_name = "logistics.shipment"
# Why: frequent status filtering benefits from a database index.
status = fields.Selection(
[
("draft", "Draft"),
("ready", "Ready"),
("shipped", "Shipped"),
],
index=True,
)
The trade-off matters. Indexing every searchable field can increase write overhead and database size. The correct approach is to inspect actual query patterns and add indexes where they support high-value access paths.
For large datasets, also review:
- ORM domains
- computed fields
- stored computed fields
- relational field access
- PostgreSQL execution plans
- batch create/write operations
- scheduled jobs processing large recordsets
Step 3: Configure Workers Around Real Concurrency
The third step is production process configuration.
Odoo's multiprocessing server is designed for production deployments, while the multi-threaded mode is primarily intended for development and demonstrations. Odoo's documentation also provides worker and memory sizing guidance based on CPU capacity and workload.
A simplified production configuration might look like:
[options]
# Why: enables multiprocessing for production HTTP workloads.
workers = 8
# Why: prevents an individual worker from consuming uncontrolled memory.
limit_memory_soft = 629145600
# Why: provides a hard safety boundary for worker memory usage.
limit_memory_hard = 1677721600
# Why: controls the maximum number of HTTP requests per worker lifecycle.
limit_request = 8192
# Why: reserves capacity for scheduled background processing.
max_cron_threads = 1
These values are examples, not universal recommendations. Worker count must be validated against CPU, memory, database capacity, transaction characteristics, and concurrency.
Increasing workers can actually expose database contention if PostgreSQL cannot process the additional concurrent workload. That is why worker tuning should follow application and SQL profiling rather than precede it.
Real-World Application
In one of our Odoo Implementation Services projects at Oodles, Paper & Pack required a production-oriented Odoo Community v17 server setup. The challenge was not simply installing Odoo. The environment needed PostgreSQL configuration, secure SSH access, Python dependencies, source-code management, configuration handling, and a repeatable deployment process.
Oodles created a terminal-based setup workflow covering server hardening with Fail2ban, PostgreSQL configuration, Odoo source deployment, Python dependency installation, and environment configuration.
Another implementation for Green Energy Africa involved Odoo modules for accounting, inventory, POS, attendance, and WhatsApp integration, with Python scripting and SQL used for customization and integration. The rollout also included five days of department-specific training, providing a concrete implementation milestone rather than treating deployment as the end of the project.
The engineering lesson is straightforward: production readiness includes infrastructure, application behavior, integrations, data, and user adoption.
You can explore more implementation work from Oodles.
Key Takeaways
- Profile before changing infrastructure. Slow SQL or inefficient ORM logic can remain slow even after adding workers.
- Batch database operations. Reducing query count is often more valuable than micro-optimising Python.
- Treat indexes as workload-specific. They improve reads but add write and storage costs.
- Size workers against concurrency and memory. Odoo's documented worker guidance is a starting point, not a substitute for load testing.
- Make deployment repeatable. Versioned configuration, dependency management, security controls, and documented setup reduce operational variance.
If you are designing a custom Odoo architecture, migrating an existing ERP, or investigating production performance problems, technical discussion is often the fastest way to identify the right implementation boundary.
Share your architecture, workload pattern, or bottleneck in the comments, or discuss your requirements with the Oodles engineering team through Odoo Implementation Services.
FAQ
1. What are Odoo Implementation Services?
Odoo Implementation Services cover the technical and functional work required to deploy Odoo for a specific organization. This can include requirements analysis, module configuration, custom development, integrations, data migration, infrastructure setup, testing, deployment, training, and post-production support.
2. How many Odoo workers should I configure?
Odoo's documentation gives CPU cores × 2 + 1 as a worker rule of thumb and estimates roughly six concurrent users per worker. Actual sizing depends on transaction complexity, memory availability, database workload, scheduled jobs, and peak concurrency, so load testing remains necessary.
3. How can I diagnose a slow Odoo module?
Start with reproducible measurements rather than changing server resources. Use Odoo's integrated profiler to inspect SQL queries and execution traces, identify repeated queries, examine expensive methods, and then retest after code or database changes.
4. Should every Odoo search field have a database index?
No. An index should be added when query patterns justify it. Odoo documentation notes that indexes can improve searches, but they consume storage and can negatively affect insert and update performance. Analyze real queries before adding indexes to custom models.
5. When should I use Odoo Implementation Services instead of configuring Odoo internally?
Use Odoo Implementation Services when the deployment involves substantial customization, integrations, migration, infrastructure decisions, complex workflows, or performance requirements. External implementation expertise can help establish architecture, automate deployment, validate workloads, and reduce risks before production.
Top comments (0)