Docker Swarm architecture comes down to a few parts: manager nodes that agree on the cluster state through Raft, worker nodes that run containers, and a service model that keeps the running containers matched to what you declared. For Odoo hosting, each part shows up in practice. It decides how many servers you can lose, what happens to running Odoo instances when a manager dies, and why a Postgres container cannot just move to another node. This guide explains the parts and what each means for an Odoo deployment.
Swarm manager and worker nodes
Every node in a swarm is either a manager or a worker:
| Manager | Worker | |
|---|---|---|
| Stores cluster state | Yes, a full Raft copy | No |
Accepts docker service, docker stack, docker node commands | Yes | No |
| Schedules tasks | The leader does | No |
| Runs containers | Yes, unless set to drain | Yes |
One manager is the leader. It makes all scheduling decisions and writes every change to the Raft log. The other managers keep copies and forward any commands they receive to the leader. Workers know nothing about the cluster as a whole. Each worker runs an agent that receives its assigned tasks from the managers, starts the containers, and reports their status back.
For Odoo hosting, set managers to drain so a heavy Odoo report or a runaway import cannot starve the control plane of CPU. Put Odoo and PostgreSQL on workers. Roles are not permanent: docker node promote and docker node demote switch them (see Docker Swarm cluster for Odoo).
Services, tasks and containers
With Swarm you do not start containers. You declare services, and Swarm creates and replaces containers to match.
- A service is the desired state: "run
odoo:18with 4 workers, 2 replicas, on nodes labeledpool=odoo". When you deploy a stack, each entry underservices:becomes a Swarm service named<stack>_<service>. - A task is one scheduled slot of a service, assigned to a node. A service with 2 replicas has 2 tasks.
- A container is what actually runs a task. If the container dies, Swarm does not restart that container. It creates a new task, often on another node, with a new container.
That last point matters for Odoo. Anything written inside a container is lost when its task is replaced. The filestore and session files under /var/lib/odoo must sit on a volume, and if a task can land on a different node, that volume must be shared storage. PostgreSQL usually gets a placement constraint that pins it to the node holding its data.
Services come in a few modes:
- replicated: a fixed number of tasks (Odoo, PostgreSQL, Traefik).
- global: one task on every eligible node (a log shipper or node exporter for monitoring).
- replicated-job / global-job: run to completion and stop, which suits a one-off Odoo module upgrade.
The managers run a reconciliation loop. They compare the desired state with what is running and fix any difference. If a node with an Odoo task goes offline, the leader schedules a replacement on a healthy node that meets the service's constraints.
Raft: how managers agree
Managers keep the cluster state (services, tasks, nodes, networks, secrets, configs) in a replicated log, and Raft keeps that log identical on every manager. Swarm does not use an external etcd cluster. SwarmKit, the orchestration engine inside Docker Engine, embeds the Raft library from the etcd project and stores the log on each manager under /var/lib/docker/swarm. The Raft log is encrypted at rest.
The basic flow:
- You run
docker service updateon any manager. A non-leader forwards the change to the leader. - The leader appends the change to its log and sends it to the other managers.
- Once a majority has written the entry, it is committed and applied. Only then does the scheduler act on it.
The leader sends heartbeats to the other managers. SwarmKit's defaults use a one-second tick, a heartbeat every tick, and an election timeout of 10 ticks. The etcd Raft library randomizes the actual timeout between that value and twice it. In practice, if the leader dies, a new one is elected after roughly 10 to 20 seconds. During that gap, running containers keep working, but no service changes or rescheduling happen.
Quorum: how many managers you can lose
Raft needs a majority of managers, (N/2)+1, to commit anything. A swarm of N managers tolerates (N-1)/2 failures:
| Managers | Majority | Can lose |
|---|---|---|
| 1 | 1 | 0 |
| 3 | 2 | 1 |
| 5 | 3 | 2 |
| 7 | 4 | 3 |
Even numbers add nothing: 4 managers still lose quorum after 2 failures, like 3 managers after 2. Docker recommends an odd number and spreading managers across at least three availability zones. Each extra manager also adds replication traffic, so 3 or 5 is the usual choice.
When quorum is lost, existing Odoo containers keep running and serving users. What stops: deploying, scaling, updating, and rescheduling tasks from failed nodes. If the lost managers cannot come back, recover by running docker swarm init --force-new-cluster on a surviving manager, then add new managers. Back up /var/lib/docker/swarm from a manager so you can restore the state if every manager is lost. For layouts that survive a zone outage, see Odoo high availability.
What runs inside a manager and a worker
On the leader, a change to a service passes through four parts in order:
- Orchestrator: compares desired and actual state and creates or removes tasks.
- Allocator: assigns overlay network IPs and virtual IPs.
- Scheduler: picks a node for each new task, filtering by constraints, available resources and availability, then spreading by placement preferences and current load.
- Dispatcher: holds connections from worker agents, sends them their tasks and receives status reports.
On each worker, the agent receives tasks from the dispatcher and the executor turns them into containers through Docker Engine. Status (starting, running, healthy, failed) flows back to the managers. That is what docker service ps shows.
Security: what Swarm gives you and what it does not
Out of the box, Swarm provides:
- Mutual TLS between all nodes. The first manager acts as a certificate authority, and node certificates are rotated automatically (every 90 days by default, adjustable with
docker swarm update --cert-expiry). - Encrypted Raft log on managers. Add
docker swarm update --autolock=trueif you want managers to need an unlock key after a restart. - Secrets delivered only to the tasks that use them, mounted in memory.
What open-source Swarm does not have is role-based access control. Anyone who can reach a manager's Docker API controls the whole cluster: every Odoo instance, every database, every secret. Protect manager sockets as you would root access. Never expose the Docker API over TCP without TLS client certificates. If a tool such as Traefik needs the API, give it read-only access, ideally through a filtering socket proxy.
For image trust, pin exact image tags or digests, pull from a private registry, and pass --with-registry-auth to docker stack deploy so workers can pull. Older guides recommend Docker Content Trust. Docker began retiring it in 2025, so use Sigstore or Notation if you need signed images.
Sizing a swarm for Odoo hosting
- Managers: 3 small nodes on fast, reliable disks, set to drain. Raft writes are synchronous, so slow disks slow down every deploy.
- Odoo workers: sized by total Odoo workers (CPU) and RAM per worker. Label them, for example
pool=odoo, and use constraints (Odoo server placement in Swarm). - PostgreSQL: pinned to a labeled node with local SSD, or kept outside the swarm on a managed database.
The pillar guide Odoo on Docker Swarm puts all the pieces together.
Easy Instance runs every customer Odoo instance as its own Swarm stack, with placement rules, Traefik routing, backups and staging handled for you. It costs $30 per Odoo worker per month with no per-user fees (pricing). Try it with a 10-day free trial, no credit card needed.