Key takeaways
-
In multi-process mode, scheduled actions run in separate cron workers, set by
max_cron_threads(2 by default). -
Each cron job has a time limit. By default it is the same as
limit_time_real, which is 120 seconds in Odoo's own defaults. - Odoo turns off a job that keeps failing, but only after at least 5 failures in a row and 7 days since the first one.
- Neutralize every copy of production, or its scheduled actions will email real customers.
What scheduled actions do in Odoo
A scheduled action (the ir.cron model) is a job that Odoo runs on a
timer. Odoo and its apps add many of them. Depending on the apps you use, they send
the queued email, create recurring invoices, send activity and payment reminders,
update currency rates, fetch incoming mail, and clean up old data.
You can see them all in developer mode, under Settings → Technical → Scheduled Actions. Each one has an interval, a next execution date, a priority and the user it runs as. Odoo stores the next execution date in UTC and shows it in your own timezone.
When scheduled actions stop, problems pile up quietly. Emails sit in the queue, invoices are not created, and nobody notices until a customer asks. So it pays to know how they run.
How Odoo runs scheduled actions
Cron workers in multi-process mode
In production, Odoo runs in multi-process mode (workers above 0). HTTP
workers serve web requests, and separate cron workers run scheduled
actions. The number of cron workers is set by max_cron_threads, which
defaults to 2 in Odoo 18 and 19. In threaded mode, used in development, the same
setting controls cron threads inside the single process.
Because cron workers are separate processes, a long job does not block web requests. But each one uses memory like an HTTP worker, so count them when you size the server.
One job runs on one worker at a time
Odoo locks a job while a worker runs it, so the same job never runs twice at once, even with several cron workers or several servers on one database. Other workers skip it and pick up the next job that is due. Jobs that have been failing are picked last.
Triggers and the next execution date
Most jobs run when their next execution date arrives. Odoo code can also trigger a job to run sooner, for example right after a user action that leaves work to do. That is why some jobs run more often than their interval suggests.
How many cron workers do you need?
For a single company database, 1 or 2 cron workers is usually enough. Consider more when:
- Several databases share one Odoo service. Cron workers serve every database the service handles, so busy databases compete for them.
- Long jobs block short ones. A big import or a bank sync can keep a worker busy while queued emails wait behind it.
- Many time-sensitive jobs are due at the same moment, such as a large batch of reminders every morning.
Do not add cron workers to fix a slow job, though. If one job takes too long, a second worker will not make it faster. Look at the job, its data volume and the database instead. Our guide to why Odoo is slow covers the usual causes, and Odoo workers and server sizing shows how cron workers count towards memory.
Time limits for cron jobs
Odoo stops a cron job that runs too long. The limit is
limit_time_real_cron. According to Odoo's own
configuration source, its default of -1 means "use limit_time_real", and
0 means no limit. Odoo's default limit_time_real is 120
seconds, which is fine for web requests but short for some jobs.
A few practical rules:
- Raise the cron limit on its own if a legitimate job needs longer, instead of raising the limit for every web request.
- Avoid no limit at all. A stuck job with no limit can hold a worker and database locks for hours.
- Prefer jobs that work in batches. Recent Odoo versions let a job report how many records it has done and how many remain. Odoo then runs it again to finish the rest, instead of doing everything in one long transaction.
limit_time_worker_cron is a separate setting: it restarts a cron worker
after a set time. Its default of 0 turns that off.
When scheduled actions fail
How Odoo handles a failing job
When a job raises an error, Odoo rolls back its changes, logs the error and schedules it again. It also counts failures. In Odoo 18 and 19, the scheduled action code turns a job off only when it has failed at least 5 times in a row and at least 7 days have passed since the first failure. A job that times out 3 times in a row without making progress counts as a failure. Any success resets the count.
That is a safety net, not monitoring. A job can fail every hour for a week before Odoo turns it off, and turning it off only stops the errors, not the problem.
How to find and fix it
- Search the Odoo log for the job's name and the error trace around its run time.
- Check the job in Scheduled Actions: is it active, and is the next execution date in the past?
- Reproduce it on a staging copy with Run Manually, so you can test a fix without touching production.
- After the fix, reactivate the job if Odoo turned it off, and watch the next run.
Scheduled actions on staging and test copies
A restored copy of production has the same scheduled actions as production. If they run, a test database can send real invoices and reminders, charge customers or sync with live systems.
Since Odoo 16, odoo-bin neutralize prepares a copy for testing. It turns
off outgoing mail servers, scheduled actions, payment providers and other
integrations. Run it on every copy before you start the service, and turn on only the
jobs you need to test. Our guide to
Odoo staging environments covers the rest
of a safe copy.
Monitoring scheduled actions
- Watch the log for cron errors and timeouts, not only HTTP errors.
- Watch the email queue. A growing queue of outgoing mail often means the mail job, or the mail server, is failing.
- Check jobs that are overdue: an active job whose next execution date is far in the past means the cron workers are stuck or too busy.
- Watch memory on the server, since heavy jobs are a common cause of memory spikes.
See what to monitor on an Odoo server for the full list.
Scheduled actions in CloudPilot
CloudPilot sets up and protects the cron side of each instance:
-
Cron workers: each instance starts with one cron worker, and you
can change
max_cron_threadsand the time limits from the instance's Config tab. Memory limits are recalculated so all workers and cron workers fit in the server's RAM. -
Time limits:
limit_time_realis 1,200 seconds andlimit_time_real_cronfollows it, so cron jobs get up to 20 minutes. -
Neutralized copies: staging copies are created with
odoo-bin neutralize, so their scheduled actions and outgoing email are off. - Logs and monitoring: each instance has a Logs tab, and server metrics and email alerts show memory and CPU trouble early.
See Create and refresh a staging copy, managed Odoo hosting for what CloudPilot looks after, and pricing for each plan.
Odoo scheduled actions: questions
How many cron workers should Odoo have?
One or two is enough for most single-database servers; Odoo's default is two. Add more when several databases share one Odoo service or long jobs keep short ones waiting, and count them when you size memory.
Why did Odoo deactivate my scheduled action?
In Odoo 18 and 19, a job is turned off after at least 5 failures in a row and 7 days since the first failure. Check the Odoo log for the error, fix it, then reactivate the job.
How do I stop scheduled actions on a test copy?
Run odoo-bin neutralize on the copy before you start it. It turns off scheduled actions, outgoing mail servers and payment providers, so the copy cannot contact real customers.