There are three ways to scale Odoo on Docker Swarm. You can add Odoo workers inside one container, add replicas of the Odoo service, or add nodes to the cluster. Each one solves a different bottleneck, and replicas come with requirements around the filestore, sessions, websockets and cron. This guide explains Odoo workers vs replicas, the docker service scale commands, and how to automate scaling when Swarm has no autoscaler of its own.
Odoo workers vs replicas
| Odoo workers | Swarm replicas | |
|---|---|---|
| What it is | Server processes inside one Odoo container (workers in odoo.conf) | Extra copies of the Odoo container, on one node or several |
| Limit | CPU and RAM of one node | The whole cluster, plus PostgreSQL connections |
| Needs shared storage | No | Yes, for the filestore and sessions |
| Survives a node failure | No, a single container | Yes, if replicas are spread across nodes |
| Change | Edit config, restart | docker service scale |
Start with workers. One Odoo container with the right worker count serves most companies. Add replicas when one node can't fit the workers you need, or when you want the instance to survive the loss of a node.
Scale Odoo with workers first
With workers above 0, Odoo runs in prefork mode. Each worker handles one HTTP request at a time, and a separate gevent process handles websocket connections. Two rules of thumb from Odoo's deployment docs:
- At most
(CPU cores × 2) + 1workers per container. - Roughly one worker for every 6 concurrent users. Heavy reports, imports and the website builder need more.
Give the container a memory limit that covers every worker's limit_memory_hard plus the cron and gevent processes, and raise the CPU limit with the worker count. The details are in Odoo server configuration.
What Odoo replicas need
Swarm's routing and Traefik spread requests across replicas, so any request can land on any container. That works only if every container sees the same state.
Shared filestore and sessions
Odoo keeps attachments in data_dir/filestore and login sessions in data_dir/sessions. Each replica with its own local volume would give users random logouts and missing images. Mount the same /var/lib/odoo into every replica from shared storage. Filestore files are named by content hash, so two replicas writing at once don't overwrite each other. Setup: Odoo filestore on NFS. Once sessions are shared, you don't need sticky sessions. Traefik can still pin clients with traefik.http.services.<name>.loadbalancer.sticky.cookie=true if you want fewer cache misses.
Websocket routing
In prefork mode, Odoo's /websocket endpoint is served on gevent_port (8072 by default), not 8069. Route it with its own Traefik router and service, as shown in deploy an Odoo docker stack. Bus notifications go through PostgreSQL LISTEN/NOTIFY, so a message created on one replica still reaches a browser connected to another.
Cron
Each replica starts max_cron_threads cron workers. Odoo locks each job in the database, so a job won't run twice, but more replicas means more processes polling for jobs. A clean layout is web replicas with max_cron_threads = 0 plus one small service with a single replica that only runs cron.
Database connections
Every Odoo process keeps its own pool of up to db_maxconn connections. The worst case is roughly replicas × (workers + 2) × db_maxconn, and it has to stay below PostgreSQL's max_connections (100 by default). Lower db_maxconn as you add replicas. If you add PgBouncer, use session pooling: Odoo's bus relies on LISTEN, which PgBouncer does not support in transaction pooling.
Module installs and upgrades
Run installs and upgrades from one container. Odoo signals registry changes through the database, so the other replicas reload their registry on their next request.
Scaling commands
# set replicas for one service
docker service scale odoo_odoo=3
# several services at once
docker service scale odoo_odoo=3 odoo_cron=1
# same result, as a service update
docker service update --replicas 3 odoo_odoo
# stop an instance without deleting it
docker service scale odoo_odoo=0
docker service scale waits until tasks converge unless you add --detach. It only works on replicated services. A mode: global service runs one task per eligible node and is scaled by adding nodes. Remember that the next docker stack deploy resets replicas to the value in the stack file, so put the number you want in deploy.replicas.
To keep replicas on different nodes, add max_replicas_per_node: 1 under deploy.placement, or spread them with preferences: - spread: node.labels.zone. Make sure there are enough eligible nodes, or extra tasks stay Pending. More in Odoo high availability.
Swarm places tasks using reservations, not actual use. If every node's reserved CPU or memory is used up, new replicas wait in Pending with no suitable node (insufficient resources), even if the nodes are mostly idle. Keep reservations realistic, especially memory.
Autoscaling Odoo on Swarm
Swarm has no built-in autoscaler, and the community autoscalers from a few years ago are no longer maintained. A setup you can maintain yourself uses tools you probably already run:
- Metrics: Prometheus scrapes container metrics (cAdvisor from
gcr.io/cadvisor/cadvisor) or Odoo response times from Traefik. See monitor Odoo. - Decision: an alert rule fires when, for example, average CPU per replica stays above 70% for 5 minutes.
- Action: Alertmanager calls a small webhook service running on a manager. That service runs
docker service scale.
Build in guardrails: a minimum and maximum replica count, a cool-down between changes, and scale-down only after a longer quiet period. The webhook needs the manager's Docker API, which gives full control of the cluster. Expose it through a socket proxy that only allows service updates, and never publish it outside the cluster.
Often the better thing to automate is nodes, not Odoo replicas. Easy Instance scales its pool of worker nodes based on the reservations of the instances it places there. Customers scale their own instance by adding Odoo workers, as described in how Easy Instance runs Odoo on Docker Swarm.
Scale without managing the cluster
On Easy Instance you add or remove Odoo workers from the dashboard. Changes apply right away with no downtime, and you pay per worker, not per user (see pricing). Try it free for 10 days, no credit card needed.