Schedule icon
Query icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon

Odoo Scheduled Action Overdue Sentinel

Count overdue Odoo ir.cron scheduled actions via XML-RPC on a schedule, change nothing in Odoo, and alert Slack when background jobs stall.

Categories
BusinessInfrastructure

Odoo runs almost every background process, invoicing automation, email queues, inventory reordering rules, through its own ir.cron scheduled-action worker, and when that worker gets stuck or dies, dozens of jobs quietly stop running at once with no error anyone sees in the UI. This blueprint turns a scheduled, read-only count into a Day 2 infrastructure health gate: io.kestra.plugin.odoo.Query asks Odoo's own XML-RPC API how many active scheduled actions are overdue past their nextcall time, a single Pebble expression classifies that count, and the result routes to a quiet log, an informational Slack note, or a Slack page depending on severity.

How it works

  1. count_overdue_crons (io.kestra.plugin.odoo.Query) runs a SEARCH_COUNT against the ir.cron model with a domain filter for active = true and nextcall <= now() - overdue_grace_minutes. The cutoff timestamp is computed entirely in Kestra with now() | dateAdd(...) | date(...) before the request is sent, so Odoo only ever has to answer a single count query. This is a read: no record is created, written, or deleted. allowFailure: true keeps an unreachable instance or bad credentials from aborting the flow outright.
  2. classify_cron_health (io.kestra.plugin.core.debug.Return) evaluates a single Pebble ternary against the returned count: missing entirely becomes UNREACHABLE, at or above critical_overdue_count becomes CRITICAL, at or above warning_overdue_count becomes WARNING, otherwise HEALTHY.
  3. route_by_cron_health (io.kestra.plugin.core.flow.Switch) branches on that classification: HEALTHY logs a quiet confirmation; WARNING posts an informational Slack note; CRITICAL and UNREACHABLE each page Slack; a defaults fallback logs the raw value for any classification outside the four known cases.
  4. log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run, independent of which Switch branch fired, so the check's own history stays queryable even if Slack delivery fails.
  5. The flow-level errors block pages Slack separately if the flow fails outright, so a broken sentinel is never mistaken for a healthy instance.
  6. The cron_overdue_schedule trigger checks every 15 minutes and ships disabled: true, with explicit inputs: overrides for overdue_grace_minutes and warning_overdue_count so the scheduled run's tolerance is pinned regardless of this flow's own defaults.

What you get

  • A scheduled, read-only background-job health check for any Odoo instance with zero custom XML-RPC scripting.
  • A single status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on.
  • Three distinct Slack messages, one per severity, each carrying the exact overdue count.
  • An audit log line on every run, separate from Slack, so the check's own history survives a webhook outage.

Who it's for

  • Teams running self-hosted or on-premise Odoo who have no alert when the cron worker process dies or hangs.
  • Operations and IT teams responsible for an Odoo instance's uptime, who need a signal before invoicing, email, or inventory automation silently stalls.
  • Anyone who has been burned by Odoo's UI looking completely normal while every background job behind it has stopped running.

Why orchestrate this with Kestra

Odoo has no built-in alert for its own cron worker going down; the first sign is usually a business process that should have happened and did not. Kestra's scheduled, read-only count check gives that signal automatically: the schedule, the threshold classification, and the Slack call are all one auditable execution history, with the Switch branch ready to grow into real remediation (restarting the Odoo cron workers, opening a ticket) without rewriting the check itself.

Prerequisites

  • A running Odoo instance reachable at odoo_url with XML-RPC enabled (on by default).
  • An Odoo user with read access to the ir.cron model.
  • A Slack incoming webhook for the WARNING, CRITICAL, UNREACHABLE, and flow-failure alerts.

Secrets

  • ODOO_USERNAME, ODOO_PASSWORD: Odoo login credentials used for XML-RPC authentication.
  • SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.

In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_, base64-encoded, and read back in flows with {{ secret('NAME') }}, for example SECRET_SLACK_WEBHOOK=$(echo -n 'https://hooks.slack.com/services/<placeholder>' | base64). This keeps credentials out of the flow YAML but, per Kestra's own documentation, offers no encryption at rest or access control beyond the host environment; use the Enterprise secrets backend for stronger guarantees.

Quick start

  1. Start a local Odoo instance with its required Postgres database, mapping Odoo's web port off host 8080 since Kestra's own UI already uses it: docker run -d --name odoo-db -e POSTGRES_USER=odoo -e POSTGRES_PASSWORD=odoo -e POSTGRES_DB=postgres postgres:15 docker run -d --name odoo-app --link odoo-db:db -p 8069:8069 odoo:17
  2. If Kestra is also running in Docker, check docker network ls for Kestra's network and attach the Odoo container to it: docker network connect YOUR_KESTRA_NETWORK odoo-app Then set odoo_url to http://odoo-app:8069. From a host-based Kestra install, use http://localhost:8069 instead.
  3. Complete Odoo's first-run database creation wizard at odoo_url, noting the database name for odoo_db.
  4. Add the ODOO_USERNAME, ODOO_PASSWORD, and SLACK_WEBHOOK secrets, and run the flow once manually to confirm status reads HEALTHY on a freshly created instance.
  5. Enable cron_overdue_schedule once pointed at a real instance.

How to extend

  • Switch the operation from SEARCH_COUNT to SEARCH_READ with fields: ["name", "nextcall"] to name the specific overdue scheduled actions in the Slack alert instead of just the count.
  • Add a second, separate check scoped to one critical cron by name (for example, the invoicing or email-queue job) with its own tighter grace period.
  • Push status into a KV store and alert only on state transitions instead of every run.
  • Chain a CRITICAL branch into a remediation step that restarts the Odoo cron workers via your infrastructure's own automation.

Inputs

  • odoo_url: base URL of the Odoo instance.
  • odoo_db: the Odoo database to connect to.
  • overdue_grace_minutes: minutes past nextcall before a scheduled action counts as overdue.
  • warning_overdue_count: overdue-count floor at or above which the check is classified WARNING.
  • critical_overdue_count: overdue-count floor at or above which the check is classified CRITICAL.

Outputs

  • status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.
  • Example path: {{ outputs.classify_cron_health.value }}, also exposed as the flow output {{ outputs.status }}.

Links

Pitfalls

  • The now() | dateAdd(...) | date(...) cutoff computation runs in Kestra's own timezone context; if the Odoo server stores nextcall in a different timezone than Kestra expects, the grace period can be effectively shorter or longer than configured. Confirm both systems agree on UTC before trusting the exact threshold.
  • This flow only ever runs SEARCH_COUNT; it never calls Odoo's CREATE, WRITE, or UNLINK operations, so there is nothing destructive to default to dry-run for.
  • A single long-interval cron (for example, one that runs once a month) can look permanently overdue under a short overdue_grace_minutes right after its last run if nextcall was computed far in the future incorrectly; check one connector's schedule before assuming the whole instance is unhealthy from a single WARNING.
  • Odoo's XML-RPC endpoint is unauthenticated transport (plain HTTP by default); use https:// in odoo_url for any instance reachable outside a trusted network.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.