How to Monitor and Profile Odoo 19 Performance: Complete Guide
Performance can become a silent issue in Odoo, especially as your database grows, more users come online, and custom modules get more complex. Users feel it as slow screens, sluggish reports, and pages that take too long to load, but guessing at the cause wastes time. The reliable approach is data-driven: monitoring shows you how the system is behaving right now, and profiling explains why. This complete tutorial is a step by step beginner guide to monitoring and profiling for Odoo 19 performance tuning, with the exact commands, configuration, and code you need to find and fix bottlenecks.
What You'll Learn:
- The difference between monitoring and profiling, and when to use each
- How to enable debug and SQL logging in Odoo 19
- How to log slow PostgreSQL queries
- How to use Odoo's built-in profiler and Python cProfile
- How to profile SQL query counts and spot N+1 problems
- Best practices to tune Odoo performance for good
Monitoring vs Profiling
These two practices are complementary. Monitoring gives real-time visibility into critical metrics, server CPU and memory, database query patterns, request response times, active users, and request volume, so you can spot issues fast and keep the system stable. Profiling goes deeper into the application to identify slow functions, measure execution time per method, and pinpoint the exact code causing slowness. Monitoring tells you something is wrong; profiling tells you what and where.
Step 1: Monitoring Techniques
Enable Debug and SQL Logging
Turn on debug logging to see what Odoo is doing, then switch to debug_sql to expose query counts, slow queries, and repeated queries that signal inefficiency.
# General debug logging
./odoo-bin --log-level=debug
# SQL-specific logging (query counts & slow queries)
./odoo-bin --log-level=debug_sql
Configure PostgreSQL Slow Query Logging
Because Odoo runs on PostgreSQL, database monitoring is essential. Log every query that takes longer than 500 milliseconds to surface expensive queries, missing indexes, and inefficient ORM patterns.
-- Log queries slower than 500 ms
ALTER SYSTEM SET log_min_duration_statement = 500;
SELECT pg_reload_conf();
Monitor System Resources
Use top or htop for live CPU and memory usage, pgAdmin for database insight, and external tools like Grafana and Prometheus for advanced visualization and alerting.
Step 2: Profiling in Odoo 19
Enable the Built-in Profiler
Start Odoo in full dev mode to enable comprehensive profiling. It reveals method execution times, call hierarchies between functions, and the specific code areas creating bottlenecks.
# Enable comprehensive profiling
./odoo-bin --dev=all
Profile Methods with cProfile
For a specific method, wrap it with a reusable cProfile decorator. The output shows execution time per function, call counts, and cumulative time across all invocations, ideal for hunting down a single slow action.
import cProfile
import pstats
import io
import functools
def profile_function(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
stream = io.StringIO()
stats = pstats.Stats(profiler, stream=stream).sort_stats('cumulative')
stats.print_stats(20)
print(stream.getvalue())
return result
return wrapper
# Usage in an Odoo model
from odoo import models
class SaleOrder(models.Model):
_inherit = 'sale.order'
@profile_function
def action_confirm(self):
return super().action_confirm()
Profile SQL Query Counts
Use Odoo's Profiler context manager to count queries inside a block. The example below shows a classic N+1 problem, a search_count executed once per record inside a loop.
from odoo import models, fields
from odoo.tools.profiler import Profiler
class ResPartner(models.Model):
_inherit = 'res.partner'
partner_count = fields.Integer(
string='Partner Count',
compute='_compute_partner_count',
store=False,
)
def _compute_partner_count(self):
count = self.env['res.partner'].search_count([])
for record in self:
record.partner_count = count
def compute_data(self):
with Profiler():
for record in self:
record.value = self.env['res.partner'].search_count([])
Common Performance Issues
Monitoring and profiling consistently surface the same culprits in Odoo systems:
| Issue | What It Means |
|---|---|
| N+1 queries | Database calls executed inside loops |
| Missing indexes | Frequently searched fields without indexes |
| Unnecessary recomputation | Computed fields recalculated more than needed |
| Inefficient ORM usage | Patterns that generate excess queries |
| Large recordsets, no batching | Processing big datasets all at once |
Best Practices for Performance Tuning
Query Smarter
Use read_group instead of looping with search, avoid queries inside loops, and add indexes to frequently searched fields.
Batch and Prefetch
Implement effective prefetching, run batch operations rather than single-record processing, and cache results when applicable.
Measure, Don't Guess
Performance tuning is an ongoing process, not a one-time job. Always rely on data-driven insights from monitoring and profiling to find the real bottleneck before changing code, then re-measure to confirm the fix.
Key Insight:
Monitoring provides visibility into current behavior, while profiling explains the underlying cause. Used together, they turn vague "the system is slow" complaints into a precise, fixable list of bottlenecks, so your Odoo 19 instance handles heavy loads with confidence.
Frequently Asked Questions
What is the difference between monitoring and profiling in Odoo?
Monitoring reveals current behavior like CPU, memory, query patterns, and response times, while profiling diagnoses the cause by measuring execution time per method to pinpoint code bottlenecks.
How do I enable SQL logging in Odoo 19?
Run Odoo with ./odoo-bin --log-level=debug_sql. This reveals query counts, slow queries, and repeated queries that signal inefficiencies such as N+1 patterns.
How do I use the built-in Odoo profiler?
Start Odoo with ./odoo-bin --dev=all to enable comprehensive profiling. It surfaces method execution times, call hierarchies, and the code areas creating bottlenecks.
What is an N+1 query problem in Odoo?
It is when database calls happen inside a loop, so one request triggers many queries. Fix it with read_group, batching, and prefetching instead of querying per record.
How do I log slow PostgreSQL queries for Odoo?
Run ALTER SYSTEM SET log_min_duration_statement = 500; then SELECT pg_reload_conf(); to log queries slower than 500 ms, exposing expensive queries and missing indexes.
Need Help Tuning Your Odoo Performance?
Our Odoo experts can profile your instance, eliminate N+1 queries, add the right indexes, and optimize custom modules so your system stays fast under load. Get a performance audit tailored to your deployment.
About the author
Odoo Practice Lead, Braincuber Technologies
Leads the Odoo practice at Braincuber. Has delivered Odoo ERP implementations, NetSuite/Tally migrations, and Shopify–Odoo integrations for US mid-market and D2C brands. Owns scoping, data migration, and go-live for every Odoo engagement.
