OCA queue_job moves long-running work out of an Odoo user's HTTP request and processes it asynchronously. That makes it useful for imports, document generation, API synchronization, and other tasks that may time out or need controlled retries. On Odoo.sh, the module can work well, but its runner configuration must match the actual build, Odoo version, worker allocation, and network route.
The configuration below is a diagnostic starting point, not a universal recipe. Test it on a staging branch and confirm it against the documentation for the exact OCA branch you deploy.
What does queue_job provide?
The module stores jobs in PostgreSQL and executes them when the runner has available capacity. A method can be delayed instead of running immediately:
record.with_delay().send_to_external_api()
The OCA module provides job states, priorities, channels, retry behavior, estimated execution times, descriptions, and related actions. Those features are useful when a temporary external failure should not block the user or lose the requested operation.
Common use cases include:
- API synchronization
- large imports and exports
- PDF or report generation
- batch updates
- warehouse integrations
- tasks that require retries after a temporary failure
The official OCA queue_job documentation is the primary reference for installation, configuration, and version-specific behavior.
Before configuring Odoo.sh
Confirm four things before editing settings:
1. The queue_job branch matches the Odoo major version.
2. The module and its Python dependencies are available in the build.
3. The module is installed in the target database.
4. The environment has enough workers for the chosen runner mode.
OCA's version 18 configuration documentation says the dedicated job runner should load queue_job as a server-wide module and run with more than one worker. Odoo.sh worker capacity is controlled through the project subscription and settings, so copying a worker value from a self-hosted example is not appropriate.
Odoo.sh containers expose a shell and an Odoo configuration file, but the platform manages the container lifecycle. Review the official Odoo.sh container documentation and test changes on staging before production.
Minimal queue_job configuration
The OCA documentation shows the following structure:
[options] server_wide_modules = web,queue_job [queue_job] channels = root:1
The exact server_wide_modules list may already contain other required modules. Preserve the existing value and add queue_job once. Do not replace platform-generated settings blindly.
root:1 is the documented default channel capacity and a conservative place to start. It allows one job in the root channel at a time. This reduces concurrency while you verify locking, transaction behavior, external rate limits, and server load.
When are scheme, host, and port needed?
The runner uses an HTTP endpoint to execute jobs. OCA supports configuration values such as:
[queue_job] scheme = https host = your-routing-host port = 443
These values are environment-specific. OCA documents defaults based on the Odoo HTTP interface and port, with localhost used when a host is not set. A public HTTPS host may solve routing in one Odoo.sh setup and be wrong in another, particularly when authentication, a custom domain, staging protection, or proxy rules are involved.
Do not assume that every Odoo.sh project should use its public domain. First inspect the runner log and the endpoint it is trying to reach. Then confirm:
- the host contains only the hostname, not
https:// - the scheme and port match the actual route
- the route reaches the correct database
- staging protection or HTTP authentication is handled
- the request does not loop through an unsuitable proxy or redirect
OCA also documents http_auth_user and http_auth_password for environments where the endpoint requires HTTP authentication. Treat those values as secrets and keep them out of Git.
How should channels be designed?
A channel limits concurrency for a class of work. Start with root:1, observe real jobs, and add capacity only when there is evidence that parallel execution is safe.
For example:
[queue_job] channels = root:2,root.api:1,root.pdf:1
This is only useful if job functions are assigned to the relevant channels. More channels do not create more Odoo.sh worker capacity, and they can introduce concurrent writes to the same records.
Create separate channels when workloads have different constraints. An external API may have a rate limit, PDF generation may consume memory, and stock updates may require strict serialization. Capacity should reflect those constraints rather than an arbitrary desire for speed.
How do you verify the runner?
After the build or service reloads, inspect the Odoo log. The OCA documentation lists startup messages such as:
queue_job.jobrunner.runner: starting queue_job.jobrunner.runner: initializing database connections queue_job.jobrunner.runner: queue job runner ready for dbqueue_job.jobrunner.runner: database connections ready
If the messages are absent, check the module path, Odoo version, installed dependencies, server_wide_modules, worker allocation, and configuration syntax before investigating business code.
For more detail, OCA recommends a queue-specific log handler:
--log-handler=odoo.addons.queue_job:DEBUG
Use debug logging temporarily. Production debug logs can be noisy and may expose operational details.
Test with a controlled job
Do not begin with a large import. Create a small idempotent test that makes an observable but harmless change in staging.
Verify that:
- the job is created
- it moves out of
pending - the runner starts it
- it finishes in
done - a deliberate temporary failure follows the expected retry pattern
- a permanent error produces a useful traceback
- repeating the job does not duplicate business data
The last point matters most. A queue can retry after a worker interruption or network failure. Code that sends an invoice, creates a shipment, or calls a payment API must prevent an uncertain retry from creating the same business effect twice.
Common causes of pending jobs
When jobs remain pending, check these causes in order:
1. queue_job is installed but not loaded server-wide.
2. The runner did not start after the build.
3. The database is not selected by the active database filter.
4. The HTTP endpoint is unreachable or misconfigured.
5. Worker capacity is unavailable.
6. The target channel has zero capacity or is incorrectly named.
7. A dependency keeps the job in a waiting state.
When jobs remain in started or enqueued, inspect whether a worker was interrupted. Current OCA documentation states that interrupted jobs in those states are automatically requeued, but confirm the behavior for your installed version.
Production safeguards
Before using the queue for important operations, add safeguards around the business method:
- make jobs idempotent where possible
- commit only through normal Odoo transactions
- use bounded retries for permanent failures
- record external request identifiers
- monitor failed jobs and queue age
- restrict access to job menus and tracebacks
- avoid putting secrets or full personal data into job descriptions
- document how operators retry or cancel work
A queue improves responsiveness, but it does not automatically make a process reliable. Reliability comes from explicit retry rules, transaction boundaries, observability, and safe handling of duplicate execution.
Recommended rollout checklist
Before production, confirm that:
- the OCA branch matches the Odoo version
queue_jobloads server-wide- the worker model supports the selected runner
- channel capacity begins conservatively
- endpoint settings match the actual Odoo.sh route
- secrets are not stored in the repository
- startup messages appear in logs
- a small staging job completes
- retry and duplicate-execution scenarios are tested
- failed jobs have an owner and a response procedure
If queue_job remains pending or your integration needs safer retry behavior, send the Odoo version, hosting model, relevant configuration without secrets, and the runner log excerpt. That is usually enough to separate a runner problem from an application-level failure.