Skip to Content

What Grafana can't tell you about your Odoo instance


Grafana is excellent software. So is Datadog, New Relic, Logz.io, and every other infrastructure monitoring stack you've probably tried on an Odoo server. None of them will catch the failure that wakes you up at 6am to an angry email from your operations manager.

This is a technical writeup, not a product pitch - although we did end up building something to close the gap, and we'll get to that at the end. If you run Odoo in production, whether for your own business or for clients, the argument below is worth reading regardless of what you decide to install.

What generic infrastructure monitoring is genuinely good at

Before we get to what Grafana misses, let's be honest about what it catches. Server-level CPU, RAM, disk IO, network throughput, load average, container health, and (with the right exporter) PostgreSQL's basic vitals - connection count, transactions per second, cache hit ratio at the database level, replication lag. If your server is dying or your database is under raw load, Grafana with the right dashboards will tell you.

We've used Logz.io and New Relic for server monitoring on production Odoo instances. Logz.io in particular gets configured to email an admin whenever ERROR-level entries appear in the application log, and the same signal can be routed to Slack or PagerDuty for higher-visibility incidents. This is genuinely useful.

This is the right layer to monitor when the server or database is the constraint. And for a plain LAMP stack or a stateless microservice, that's often enough - the application is basically a thin skin over infrastructure.

Odoo is not that.

Where the gap opens

Odoo is an application server, an ORM, a workflow engine, a scheduled-task runner, a mail queue processor, and a database schema all bundled into a single process pool. Every one of those components can be silently broken while the underlying server looks perfectly healthy. What follows is not exhaustive - it's the failure modes we see most often in production Odoo instances, none of which any generic infrastructure monitor will surface without heavy custom instrumentation.

1. Cron reliability

Odoo runs recurring business logic through ir.cron: invoice reminders, mail queue flushing, calendar sync, inventory reordering, stock quant reconciliation, subscription renewals, custom automation. When one silently fails, the business logic simply stops. The server stays at 40% RAM. Grafana shows green.

What's worse: Odoo has a defence mechanism (in versions 18 and 19). If a scheduled action fails 5 times in a row over a period of at least 7 days, the framework marks it inactive to protect the worker pool. The logic lives in _update_failure_count() in odoo/addons/base/models/ir_cron.py. This is defensible behaviour: better to stop a failing cron than let it exhaust workers. But the only signal is a single admin notification email, and in most deployments that address either goes to a shared mailbox nobody reads or gets caught by a spam filter. Odoo 17 and earlier don't auto-deactivate at all — a failing cron there retries indefinitely, logging a traceback each time. We wrote a separate deep-dive on the three cron failure modes if you want the SQL to catch each one.

One client had a customised cron that pushed purchase orders to vendors automatically every couple of hours. The POs were operationally critical - vendors depended on them for timely delivery scheduling. The cron silently failed for a full day. Nobody caught it internally. It surfaced only when an employee called a vendor to confirm a delivery and the vendor said they hadn't received the PO in the first place. A day of production output was already at risk by the time anyone knew.

You can query for this state directly:

-- Odoo crons that should have run recently but haven't
SELECT name, active, nextcall
FROM ir_cron
WHERE active = false
   OR nextcall < NOW() - INTERVAL '2 hours'
ORDER BY nextcall;

But you have to know to look. Nobody's Grafana dashboard has this query on it.

2. The mail.mail queue

Odoo queues outbound email in mail.mail and flushes it via a cron. When the SMTP server rejects a message, when outgoing credentials expire, when a specific recipient triggers a hard bounce - the message sits in mail.mail with state = 'exception' and a traceback tucked into failure_reason. The cron keeps running. The queue keeps growing. Nobody notices until a customer asks why they didn't get their invoice.

The usual causes we see: third-party ESPs having a bad week (Mailchimp, Mailgun, SendGrid - they all do occasionally), expired SMTP credentials nobody remembered would rotate, or SPF/DKIM records drifting after a domain change. We've seen backlogs cross a hundred undelivered messages before anyone flagged it, usually because a salesperson followed up on something unrelated and noticed a customer hadn't seen the previous email either.

SELECT COUNT(*) AS failed_emails,
       MAX(create_date) AS newest_failure
FROM mail_mail
WHERE state = 'exception';

3. Per-module performance attribution

You have a slow Odoo instance. Which of your 47 installed modules is responsible?

Grafana can show you that PostgreSQL is running slow queries. It can't tell you that hr_expense is behind 30% of them, that account.move.line lookups are exploding because of a custom module's Python override, or that a calendar sync add-on is opening a new database cursor every 90 seconds.

The information exists inside Odoo - the ORM knows which model was queried, the traceback knows which add-on raised the exception. Surfacing it requires walking the Python stack for every logged error and mapping it back to the module that installed the offending file. That's an application-level concern; infrastructure monitoring can't do it because it has no view into the Python process's call stack.

4. Filestore vs database drift (ghost files)

Odoo stores binary attachments on disk in the filestore, with a checksum reference in the ir_attachment table. Over time these drift out of sync. Records get deleted but the file lingers. An upload half-completes and writes the file but not the DB record. A module upgrade renames an attachment model. The result is a filestore directory that grows unbounded with files no attachment record references.

Most agencies don't audit for this. We didn't, until we built the module - the pattern isn't loud enough to make anyone open psql on a random Tuesday. Which is exactly the point of surfacing it on a dashboard.

Infrastructure monitors just see disk usage growing linearly and shrug.

5. Container ceiling vs host memory

This one is subtle enough to trip experienced engineers. If you run Odoo in Docker, psutil.virtual_memory() reports the host's memory, not the container's cgroup limit. Every "you have 32 GB free" reading in a containerised deployment can be a lie. The container might be at its cgroup ceiling of 4 GB while the host has 28 GB spare, and half the monitoring stacks in the wild won't notice.

Grafana with the right node exporter can be configured correctly for this - but most defaults don't do it right. The fix is to read /sys/fs/cgroup/memory.max (cgroup v2) or /sys/fs/cgroup/memory/memory.limit_in_bytes (v1) and report against that, not against the host's /proc/meminfo.

This matters more than it sounds. In the Odoo world it's routine for bad customisations, memory leaks in custom code, or unoptimised inheritance chains to eat RAM until the container hits its ceiling. The reflex is to upgrade infrastructure instead of hunting down the actual leak - it's the more expensive fix, and it doesn't fix the underlying problem. When we see it at a client, it's usually the first flag that a proper Odoo performance audit would pay for itself in months.

6. Configuration and module upgrade drift

Somebody bumps limit_time_real from 120 to 600 in the config file to work around a slow report. Six weeks later, cron jobs that used to time out now silently hold connections for ten minutes each. Or: a colleague installs a module through the UI without telling anyone; that module ships a new cron, a new indexed field, a new inheritance on account.move. Weeks later something breaks and nobody remembers what changed.

Infrastructure monitoring has no concept of "an Odoo module was installed" or "a system parameter was edited." These are application-level state changes with real operational impact, and they need application-level audit trails.

Why the gap exists

None of this is a critique of Grafana. It's a critique of using infrastructure monitoring to answer application questions. They work at different layers.

Infrastructure monitoring answers "is the box up?" - OS-level counters, systemd unit health, HTTP status codes. It's the right tool for questions like "is the server healthy?" or "is nginx returning 200s?"

Application monitoring answers "is the software doing what it's supposed to do?" For Odoo specifically that means: did the scheduled action fire, did the mail queue drain, is the ORM performing reasonably, are configuration parameters within safe bounds, is the filestore consistent with the database. None of those can be answered by looking at the host - you have to look into Odoo's own state.

Our honest take: it's a structural problem. Odoo ships baseline logging and a few warning mechanisms but nothing that qualifies as an application-level operational dashboard. The general-purpose monitoring vendors - Grafana, Datadog, New Relic - build for the largest possible market, which means server-shaped problems, because servers are the same everywhere. Odoo SA is busy shipping features. Agencies are busy shipping custom code. When things go wrong in an Odoo instance, there's no one whose job is to look at Odoo's own state and prevent it from recurring, so people default to the easy options: bump the instance size, restart the workers, hope the problem doesn't come back. It usually does.

What we built

We got tired of debugging silent Odoo failures with grep and psql, so we built a module that puts all six of the above (plus about forty other checks) on a single dashboard inside Odoo. It's called Odexalabs Vigil Suite and it's on the Odoo App Store for $129 one-time.

The design principle is that every alert has to be actionable - you should be able to see the problem, understand it, and take the next step without leaving Odoo. Three-tier cron detection (overdue, silently failed, auto-deactivated) with drill-down to the traceback. A live intercept of long-running PostgreSQL queries from pg_stat_activity with a kill-stuck-backend button. Module impact ranking that scores which of your installed modules is measurably degrading the instance. Automated cloud backup to seven destinations, with chunked uploads for databases past 10 GB. RAM tracking that reads cgroup limits correctly. The full feature list is on the product page, and if you want to see every screen with configuration guidance, the user guide covers every panel.

Grafana isn't a competitor and we still recommend running it (or equivalent) for the infrastructure layer. What Vigil adds is the layer above. On the day it went live at one client, we could see exactly which of their installed modules was hammering PostgreSQL, drill into that module's code, and find the custom loop that was firing a heavy query on every iteration. Infrastructure monitoring told us the box was under load. Vigil told us why. Use both.

FAQ

Isn't Odoo's own logging enough to catch cron failures?
Not for silent failures. Odoo logs the exception when a cron raises, but if nobody is reading the logs - and nobody is, in most production instances - the failure sits there. Even setups that route ERROR-level log entries to email will miss the moment Odoo auto-deactivates the cron, because that's flagged as a status change, not an error entry.

Can Grafana be configured to alert on Odoo-specific issues?
Yes, with enough custom work. You can write a Postgres exporter to expose ir_cron, mail_mail, and ir_attachment statistics as metrics, build Grafana panels on top, define alert rules, and maintain the whole thing. In practice we've almost never seen an agency actually do this end-to-end, and it doesn't cover application-level concerns like module impact attribution or filestore drift because Postgres doesn't have that information.

Do I need to install anything on my Odoo server to detect these issues?
For raw diagnosis you can get quite far with a psql client and the queries in this post. For continuous monitoring you either build custom exporters and dashboards yourself, or install a module like Vigil that ships the dashboards inside Odoo.

Which Odoo module in Community or Enterprise ships this by default?
None. Odoo Community and Odoo Enterprise both include basic logging and a few internal warning mechanisms, but neither ships a comprehensive operational dashboard for cron reliability, mail queue health, per-module performance attribution, filestore integrity, or configuration change tracking.

Does this apply to Odoo.sh customers too?
Yes. Odoo.sh manages the server layer for you, but the application-layer failure modes covered in this post - silent crons, mail queue backlogs, per-module performance, configuration drift - are all still yours to catch. Odoo.sh's built-in dashboards don't cover them.

Related reading

7 Silent Killers of Odoo Performance (And How to Catch Them Before Your Users Do)