CertGrid CertGrid
Concepts·Docker

Docker Swarm Cluster Architecture

Docker's own orchestrator, already in the engine you have installed. Managers, workers, the raft state store and the two join tokens - read off a real three-node cluster rather than a diagram.

Swarm and Multi-Host (optional) Guide 41 of 46 Intermediate

Tested on the versions above. Captured on a real three-node cluster. Node IDs, IP addresses and join tokens are specific to that cluster - tokens shown here are truncated deliberately and were rotated afterwards. Match the shape of the output, not the values.

Three hosts, because a swarm needs more than one. DOCKER01 is the single manager and Leader; the other two are workers. One manager means no fault tolerance, which several of these guides make a point of - three would tolerate one failure.
Server NameIP AddressOSRolesCPURAMHDD
DOCKER01192.168.0.21Ubuntu 26.04 LTSSwarm Manager (Leader)2 Core4 GB50 GB
DOCKER02192.168.0.22Ubuntu 26.04 LTSSwarm Worker2 Core4 GB50 GB
DOCKER03192.168.0.23Ubuntu 26.04 LTSSwarm Worker2 Core4 GB50 GB

Before you start

  1. Why swarm exists, and where it fits

    Compose runs several containers on ONE machine. Swarm runs services across SEVERAL, and it is built into the engine - no extra install, no control plane to operate. It is far smaller than Kubernetes in both capability and effort, which is exactly the trade: for a handful of machines running a handful of services, it is often the right amount of orchestrator.

    bash Example session
    docker info --format "swarm={{.Swarm.LocalNodeState}} manager={{.Swarm.ControlAvailable}} nodes={{.Swarm.Nodes}} managers={{.Swarm.Managers}}"managers=1 nodes=3

    Expected resultswarm=active on a node that has joined a cluster.

    Success conditionYou can tell a swarm node from a standalone one with a single command. inactive means this engine is not in a cluster.

  2. Start a cluster, or join one

    One command creates a cluster and makes that machine its first manager. Everything else joins with a token. --advertise-addr matters the moment a host has more than one address - swarm has to tell other nodes where to find it, and it will refuse rather than guess.

    bash
    docker swarm init --advertise-addr 192.168.0.21Swarm initialized: current node (k64c8bnn79muxpj6m0qf872nb) is now a manager. To add a worker to this swarm, run the following command:     docker swarm join --token SWMTKN-1-3g4ak6liblug30tvups4qiof941gqo37zacd3sbsaoi5ciit1f-apti3pb5rhn0pdgw8zhuejjmm 192.168.0.21:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

    Expected resultA confirmation naming the new manager node.

    Success conditiondocker node ls works. It only works on a manager - that is the quickest way to tell which role you are on.

  3. Read the cluster

    docker node ls is the cluster's inventory, and it runs only on a manager. STATUS is whether the node is reachable, AVAILABILITY is whether it may be given work - two different things that people conflate, and the distinction matters in guide 45.

    bash Example session
    docker node lsID                            HOSTNAME       STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSIONfr31hknhlxd5a9s4324qwqqy1 *   ahm-docker01   Ready     Active         Leader           29.7.2y9vvv2k6w5ur7kjodwnro59ej     ahm-docker02   Ready     Active                          29.7.2vqacq9wzoqon1nkz9bd9m0l3j     ahm-docker03   Ready     Active                          29.7.2

    Expected resultEvery node listed, with one marked Leader and * marking the node you are on.

    Success conditionYou can identify the leader and your own node. Running this on a worker fails - that refusal is diagnostic, not a problem.

  4. Managers hold the state; workers run the work

    A manager stores the desired state of the whole cluster in a raft log and decides where tasks run. A worker just runs what it is told. Managers also run tasks by default, which is fine for a small cluster and worth turning off for a large one. Inspect shows the split plainly.

    bash Example session
    docker node inspect ahm-docker01 --format "role={{.Spec.Role}} avail={{.Spec.Availability}} addr={{.Status.Addr}} leader={{.ManagerStatus.Leader}} reachability={{.ManagerStatus.Reachability}}"role=manager avail=active addr=192.168.0.21 leader=true reachability=reachabledocker node inspect ahm-docker03 --format "role={{.Spec.Role}} avail={{.Spec.Availability}} addr={{.Status.Addr}} cpus={{.Description.Resources.NanoCPUs}}"role=worker avail=active addr=192.168.0.23 cpus=2000000000

    Expected resultOne manager with a ManagerStatus, one worker without.

    Success conditionA worker has no .ManagerStatus at all - referencing it on a worker produces nothing, which is how you can test the role in a script.

  5. Raft, and why manager count is odd

    Managers agree on cluster state through raft, which needs a majority to make decisions. Three managers tolerate one failure; five tolerate two. An EVEN number buys nothing - four managers still only tolerate one, because three of four is the majority either way. One manager, as in this cluster, means no fault tolerance at all: lose it and the cluster keeps running but you cannot change anything.

    bash
    docker info --format "managers={{.Swarm.Managers}} nodes={{.Swarm.Nodes}}"managers=1 nodes=3

    Expected resultThe manager count for your own cluster.

    Success conditionYou can state your failure tolerance as a number. Running tasks survive a manager outage; scheduling, scaling and deploys do not.

  6. The two join tokens are two different privileges

    There is a worker token and a manager token, and they are not interchangeable. Anyone holding the manager token can take administrative control of your cluster, so it belongs with your other high-value credentials. If one leaks, rotate it - the old token dies immediately and existing nodes stay joined.

    bash Example session
    docker swarm join-token worker    docker swarm join --token SWMTKN-1-1wodmz4m2o1415q...REDACTED 192.168.0.21:2377# tokens are credentials - these are truncated, and the real ones were rotated after capturedocker swarm join-token --rotate worker -qSWMTKN-1-1wodmz4m2o1415q...  # a new token; the previous one no longer works

    Expected resultA ready-to-paste join command, and a new token after rotating.

    Success conditionYou treat the manager token as an admin credential. Note port 2377 - the cluster management port, which must be reachable between nodes.

  7. What has to be open between nodes

    Three ports, and a cluster that half-works is almost always one of them being blocked. 2377 is management, 7946 is node-to-node discovery and must be open on BOTH TCP and UDP, and 4789 UDP carries overlay traffic. A firewall allowing only 2377 gives you a cluster that forms and then cannot route anything.

    bash Example session
    sudo ss -ltnp | grep -E "2377|7946"LISTEN 0      4096               *:7946            *:*    users:(("dockerd",pid=63915,fd=31))LISTEN 0      4096               *:2377            *:*    users:(("dockerd",pid=63915,fd=26))

    Expected resultdockerd listening on the management and discovery ports.

    Success conditionYou know which three ports to check first when nodes join but services cannot talk.

Troubleshooting

Official sources