Symptoms
- Emails stuck in queue (not being sent)
- Subscriptions not renewing
- Inventory rules not triggering
- Automated actions not firing
- Scheduled reports not generating
Cause 1: workers=0 (Most Common)
# In single-thread mode (workers=0), cron jobs share
# the same thread as HTTP requests.
# If the server is busy, crons may not run.
# Fix: enable multi-process mode
# odoo.conf:
workers = 5 # (CPU × 2) + 1
# With workers > 0, Odoo spawns a dedicated cron worker
# that processes scheduled actions independentlyCause 2: Cron Disabled
# Check if the specific cron is active:
SELECT name, active, interval_number, interval_type,
lastcall, nextcall, numbercall
FROM ir_cron
WHERE name LIKE '%Email%';
# If active = false:
UPDATE ir_cron SET active = true WHERE id = XXX;
# If numbercall = 0 (exhausted its run count):
UPDATE ir_cron SET numbercall = -1 WHERE id = XXX;
# -1 means unlimited runs
# In Odoo UI:
# Settings → Technical → Scheduled Actions
# Find the cron → check Active checkbox
# Set Number of Calls to -1 (unlimited)Cause 3: Cron Crashed and Stopped
# If a cron throws an unhandled exception, it may stop running.
# Check the Odoo log for errors:
grep -i "cron" /var/log/odoo/odoo.log | grep -i "error" | tail -20
# Common cron errors:
# - AccessError (permissions changed)
# - RecordNotFoundError (deleted record referenced)
# - ConnectionError (external API down)
# - MemoryError (cron processing too much data)
# Fix: fix the error in the cron code, then reactivateCause 4: Database Lock
# Another process holds a lock that blocks the cron:
SELECT pid, state, query, now() - query_start as duration
FROM pg_stat_activity
WHERE datname = 'odoo' AND state != 'idle'
ORDER BY duration DESC;
# Check for advisory locks (used by some crons):
SELECT * FROM pg_locks WHERE locktype = 'advisory';
# Fix: terminate the blocking query
SELECT pg_terminate_backend(PID);Cause 5: nextcall Far in the Future
# The cron's next execution time is set far in the future:
SELECT name, nextcall FROM ir_cron
WHERE nextcall > NOW() + INTERVAL '1 day'
ORDER BY nextcall;
# Fix: reset nextcall to now
UPDATE ir_cron SET nextcall = NOW() WHERE id = XXX;Cause 6: Time Zone Issues
# Odoo stores cron times in UTC.
# If your server timezone changed, nextcall may be off.
# Check server timezone:
timedatectl
# Odoo should always run in UTC:
# odoo.conf or systemd service:
# Environment="TZ=UTC"Cause 7: max_cron_threads
# In Odoo 16+, max_cron_threads controls how many crons run simultaneously
# Default: 2
# If you have many crons, increase:
# odoo.conf:
max_cron_threads = 4Diagnosis Checklist
- Check
workersin odoo.conf — must be > 0 for reliable crons - Check
activeandnumbercallin ir_cron table - Check Odoo logs for cron errors
- Check
lastcall— is it recent or stuck in the past? - Check
nextcall— is it in the future or past? - Check for database locks blocking execution
- Check server timezone — should be UTC
- Restart Odoo and monitor logs
Monitoring Cron Health
# Alert if any cron hasn't run in 2× its interval:
SELECT name, lastcall, nextcall,
interval_number || ' ' || interval_type as interval,
CASE WHEN lastcall < NOW() - (interval_number *
CASE interval_type
WHEN 'minutes' THEN INTERVAL '1 minute'
WHEN 'hours' THEN INTERVAL '1 hour'
WHEN 'days' THEN INTERVAL '1 day'
END * 2)
THEN 'STUCK' ELSE 'OK' END as status
FROM ir_cron
WHERE active = true;DeployMonkey
DeployMonkey's AI agent monitors cron health automatically. It detects stuck crons, identifies the root cause (disabled, error, lock), and alerts you with the specific fix. Cron issues are diagnosed in seconds, not hours.