HostiServer
2026-10-14 11:19
Docker Swarm: A Cluster of Multiple Nodes on a VPS
📚 "Docker Swarm" mini-series, part 1 of 2:
- Docker Swarm: a cluster of multiple nodes ← you are here
- Docker Swarm in Production: Secrets, Stack Deploy, and Portainer
Docker Swarm: a cluster of multiple nodes
The main "Docker on a VPS" series ended on a logical hint: everything we built - from the first container to a self-hosted Git with its own CI - ran on a single server. A single server means a single point of failure: if the VPS goes down, the entire stack goes down at once, no matter how many restart: unless-stopped entries are in the compose file.
Docker Swarm is the orchestrator built into Docker Engine that solves exactly this problem: it distributes services across multiple nodes and itself makes sure the desired number of replicas stays running, even if one of the nodes fails. The key difference from everything we've done so far is that there are no external dependencies at all. There's no need to install Kubernetes, etcd, or a separate control plane: the cluster comes up with a single docker swarm init command on the same Docker Engine that's already running on the server from the first article of the series.
Swarm makes sense in a specific niche: a cluster of 2-10 nodes, a team with no Kubernetes experience, infrastructure on a VPS or bare metal without a cloud provider offering a ready-made managed cluster. Beyond that niche - large clusters, complex stateful workloads, a need for autoscaling - the conversation shifts to Kubernetes, but that's a topic for another conversation.
This mini-series consists of two articles. The first is about the cluster itself: nodes, services, overlay networks, rolling updates with no downtime. The second is about what's needed for real-world use: secrets and configs instead of a plain-text .env, docker stack deploy for deploying compose files as a whole stack, and Portainer as a graphical interface on top of all of it.
2. Swarm vs standalone Docker Compose
In articles 1-5 of the main series, Docker Compose managed containers on a single host. There was no failover and no load distribution across servers - if the host went down, all services went down at once, and they could only be brought back up on that same host, once it was reachable again.
Swarm doesn't require rewriting everything from scratch. The same compose-file principles - services, networks, volumes - carry over almost unchanged; the difference is in the deployment command: instead of docker compose up -d, you use docker stack deploy, which distributes services across the cluster's nodes instead of bringing them all up on a single host.
The main conceptual difference is in exactly what Docker keeps track of. In plain Compose, the basic unit is the container: a specific running process on a specific host. In Swarm, the basic unit is the service: a declaration of a desired state along the lines of "keep 3 replicas of image X running, regardless of which nodes they're on." A container is a fact; a service is an intent, which Swarm maintains on its own, restarting replicas wherever possible if something goes wrong.
3. Key concepts
3.1 Node
A node is a physical or virtual machine that's part of the cluster. Every node has one of two roles: manager or worker.
3.2 Manager
A manager stores the state of the entire cluster as a Raft log, accepts management commands (docker service create, docker stack deploy), and schedules which nodes run tasks on. The minimum is one manager node; for fault tolerance of cluster management itself, you need an odd number - 1, 3, or 5.
3.3 Worker
A worker executes tasks assigned by manager nodes, but doesn't itself accept cluster-management commands. In a simple topology, a manager node can also run workloads at the same time as managing the cluster, but for production clusters with multiple nodes, managers are usually relieved of application services.
3.4 Service
A service is a desired state: "keep N replicas of image X running and reachable on port Y." Swarm maintains this state over time on its own - if a replica goes down, it starts a new one in its place, without waiting for human intervention.
3.5 Task
A task is one specific instance of a service on a specific node, effectively the equivalent of a single container. If a service has 3 replicas, the cluster has 3 tasks at once, possibly spread across different nodes.
3.6 Overlay network
An overlay network is a virtual L2 network on top of the L3 connection between the cluster's nodes. Services connected to the same overlay network see each other by service name regardless of which physical node each of them actually runs on - the same way Compose services saw each other by name within a single host in article 2 of the main series.
3.7 Raft consensus
Manager nodes agree on the cluster's state among themselves through the Raft consensus algorithm. Quorum - the minimum number of manager nodes that must stay online for the cluster to keep making decisions - is calculated as (N/2)+1. For 3 manager nodes, the quorum is 2: the cluster can tolerate the loss of one manager node and keep working.
4. Requirements and preparation
4.1 Minimal topology
For a first introduction, two virtual machines with Docker Engine installed per article 1 of the main series are enough: one becomes the manager, the other the worker.
4.2 Ports between nodes
For the cluster to work, three ports must be open between all nodes:
| Port | Protocol | Purpose |
|---|---|---|
| 2377 | TCP | Swarm management - cluster management commands |
| 7946 | TCP/UDP | Node discovery - nodes discovering one another |
| 4789 | UDP | Overlay network (VXLAN) - traffic between containers on different nodes |
4.3 A single Docker Engine version
Every node must run the same Docker Engine, installed via the official script - no Docker Desktop (it isn't meant for servers, as discussed in the first article of the series) and no mismatch in the Compose plugin version between nodes. A mismatch in Engine versions between manager and worker is a rare but real cause of odd cluster behavior that's hard to diagnose after the fact.
5. Initializing the cluster
5.1 The first node becomes the manager
docker swarm init --advertise-addr <manager_node_IP>
The --advertise-addr flag specifies exactly which IP address other nodes should use to connect to this manager - it's important to set it explicitly if the server has several network interfaces, so the cluster doesn't try to communicate over a private address that's unreachable from outside, or vice versa.
5.2 Join tokens
After running the command, Docker immediately prints two tokens - one for joining worker nodes, one for joining additional manager nodes. The tokens are long strings that act as a password for joining the cluster; they should be stored just as carefully as the deployment SSH keys from article 4 of the main series.
5.3 Getting a token later
If the terminal with the command's output has already been closed, the tokens can be retrieved again at any time:
docker swarm join-token worker
docker swarm join-token manager
6. Adding nodes
6.1 Joining a worker node
On every new node that needs to be added as a worker:
docker swarm join --token <worker-token> <manager-ip>:2377
6.2 Joining an additional manager
The same command, but with the manager-token instead of the worker-token. For clusters where fault tolerance of the management itself matters, not just the workloads, it's recommended to keep 3 or 5 manager nodes - in line with the quorum formula from section 3.7.
6.3 Checking the cluster's composition
docker node ls
This command only runs on a manager node and shows all the cluster's nodes along with their role (Manager / Worker) and current status (Ready, Down, etc.) - the first tool to reach for when something in the cluster isn't behaving as expected.
7. Services: launching and scaling
7.1 Creating a service
docker service create --name app --replicas 3 --publish 80:3000 myimage
This command is the conceptual equivalent of docker run from article 1 of the main series, but at the cluster level: instead of a single container on a single host, Swarm itself distributes three replicas across the available nodes, guided by its own scheduling algorithm.
7.2 Viewing service status
docker service ls
docker service ps app
docker service ls shows a list of all the cluster's services with the number of ready replicas. docker service ps app drills down: exactly which node each specific replica of the app service lives on, and what state it's in.
7.3 Scaling in real time
docker service scale app=5
The number of replicas changes without recreating the service and without any downtime for the replicas that are already running - Swarm simply adds or removes the needed number of tasks, adjusting the real state to match the new desired value.
7.4 The built-in ingress load balancer
The --publish flag in Swarm works differently from plain docker run. Any node in the cluster, even one with no replica of the service physically running on it, accepts incoming traffic on the specified port and proxies it itself to one of the live replicas - regardless of which node that replica actually runs on. This removes the need for an external load balancer in the basic scenario: you can reach the service via the IP of any node in the cluster.
8. Overlay network
8.1 Creating an overlay network
docker network create --driver overlay mynet
Unlike the bridge network from article 2 of the main series, which exists only within a single host, an overlay network is available across all the cluster's nodes at once.
8.2 DNS between services
Services connected to the same overlay network resolve each other by service name through the built-in Swarm DNS - the same principle as in Compose (article 2), except now it works across physical servers, not just within a single host.
8.3 Encryption and isolation
Traffic between nodes inside an overlay network is encapsulated in VXLAN and can optionally be encrypted. Isolation between different overlay networks works on the same principle as isolation between bridge networks in Compose: a service on one network doesn't see a service on another, even if they're running on the same physical nodes.
9. Rolling update
9.1 Updating a service's image
docker service update --image myimage:v2 app
Unlike docker compose up -d --no-deps app from article 4 of the main series, where updating the application meant a brief but all-at-once restart of every replica, Swarm replaces replicas gradually, one group at a time, without stopping the whole service at once.
9.2 Controlling the pace of the update
| Parameter | Purpose |
|---|---|
--update-parallelism |
How many replicas are updated at once in a single group |
--update-delay |
The pause between updating one group of replicas and the next |
9.3 Automatic rollback on failure
The --update-failure-action rollback flag enables an automatic return to the previous image version if the new replicas fail to come up successfully. This is a direct extension of the manual rollback idea via a SHA tag from article 4 of the main series - except now the decision to roll back is made by the cluster itself, not by a person running a command in a terminal.
9.4 The result: zero downtime without an external load balancer
Combining the gradual replacement of replicas with the built-in ingress load balancer from section 7.4 gives a genuine zero-downtime update - the level that in article 3 of the main series could only be achieved with a complex blue/green scheme involving a manual Nginx switch.
10. Self-healing and Swarm's limitations
10.1 Node failure
If a node becomes unreachable, Swarm reschedules all the tasks that were running on it onto other available nodes in the cluster - provided the manager node quorum (section 3.7) is still maintained.
10.2 Failure of an individual task
If the process itself inside a task container crashes, Swarm restarts it according to the --restart-condition policy - conceptually the same mechanism as restart: unless-stopped in Compose, just applied at the cluster level rather than a single host.
10.3 Honest limitations compared to Kubernetes
Swarm is deliberately simpler than Kubernetes, and it's worth understanding that upfront rather than discovering it after the fact. What's missing: horizontal pod autoscaling (automatic scaling based on load), a full health-check API comparable to Kubernetes probes, a CRD-and-operators mechanism for extending cluster behavior, and support for stateful services (databases inside the cluster) is noticeably weaker than in the Kubernetes ecosystem with its StatefulSets and persistent volume claims.
10.4 When Swarm is genuinely enough
Where you need a small production cluster without a cloud provider, and where operational simplicity matters more than flexibility, Swarm remains a sensible choice. A team with no Kubernetes experience gets fault tolerance and rolling updates with literally one docker swarm init command, without the learning curve a full Kubernetes cluster demands.
11. Conclusion
By the end of this article, you have a working cluster of at least two nodes, a service running on several of them at once, and a rolling update that updates the image version with no noticeable downtime for users. That's already a meaningful step forward from the single VPS at the end of the main series: one node going down no longer means the whole stack going down.
A practical question remains open: how to deploy not a single service via docker service create, but an entire stack of several services at once; how to store database passwords not in a plain-text .env, but through a mechanism built specifically for secrets; and how to look at all of this not from the command line, but through a graphical interface. That's exactly what the second, final article of the mini-series covers.
📚 Mini-series navigation:
You're reading part 1 of 2, "Docker Swarm: a cluster of multiple nodes."
Next: Part 2. Docker Swarm in Production: Secrets, Stack Deploy, and Portainer →
🚀 Several VPS for your Swarm cluster
Swarm distributes load across nodes, which means it needs several independent servers rather than one powerful one. Hostiserver provides resources well suited to exactly this kind of topology.
🖥️ Dedicated Servers
- From $90/mo, full control over the hardware for heavy cluster worker nodes
- No resource neighbors: Swarm's task scheduling relies on a node's real capacity, not shared capacity
- 24/7 support: we'll help with networking between nodes and setting up ports 2377/7946/4789
💻 Cloud (VPS) Hosting
- From $19.95/mo, KVM isolation, dedicated vCPU and RAM - equally for manager and worker nodes
- Stable internal network: critical for Raft consensus between manager nodes and for overlay traffic
- Easy to deploy the same image to several VPS at once for new worker nodes
- Easy to scale: adding another node to the cluster is just another VPS on the same plan
💬 Not sure which option you need?
💬 Write to us and we'll help you decide!
Frequently Asked Questions
- Docker Swarm or Kubernetes - where should a team with no orchestration experience start?
Swarm is a sensible first step for a cluster of 2-10 nodes: it's built into Docker Engine, requires no separate control-plane installation, and comes up with a
docker swarm initcommand. Kubernetes is justified when you need horizontal autoscaling, complex stateful workloads, or a cluster growing well past a dozen nodes - but the price for that, the learning curve, is noticeably steeper.
- Can you make a cluster out of a single manager node?
Technically, yes -
docker swarm initon one machine already creates a full (if single-node) cluster. But the whole point of Swarm is fault tolerance across several nodes; with one manager node, you lose both load distribution and protection against a single server's failure. For real fault tolerance you need at least 2 nodes, and for a resilient management quorum, 3 manager nodes.
- What happens to the cluster if the manager node quorum is lost?
The cluster stops accepting new management commands (creating services, making changes) until the quorum is restored - although tasks already running on worker nodes keep working. That's exactly why an odd number of manager nodes (3 or 5) is recommended for production: it gives you a margin that lets you lose one node and still keep the quorum.
- Is a separate load balancer needed in front of a Swarm cluster?
For the basic scenario - no. The built-in ingress load balancer itself accepts traffic on the published port on any node in the cluster and proxies it to a live replica of the service, regardless of where that replica is physically running. An external load balancer like Nginx or HAProxy in front of the cluster makes sense when you need extra capabilities - for example, SSL termination at a single point, or distributing traffic across several Swarm clusters.
- Do the compose files from articles 1-5 of the main series carry over into Swarm unchanged?
Mostly, yes - services, networks, and volumes are described with the same syntax. The main difference isn't in the file itself but in the deployment command: instead of
docker compose up -d, you usedocker stack deploy, and some directives (such asbuild:) aren't supported in Swarm - the stack has to be deployed from an already-built image in a registry, on the same principle as the auto-deploy from article 4 of the main series. The second part of this mini-series coversstack deployin detail.
- How does Swarm decide which node to place a new service replica on?
By default, Swarm's scheduler distributes replicas evenly across the available nodes, taking current load into account. For finer control you can set constraints - for example, pinning a service to nodes with a specific label (SSD disk, a specific region) - but for basic use, scheduling happens fully automatically, with no extra configuration.
- What happens if one of the new replicas fails to start during a rolling update?
The behavior depends on the
--update-failure-actionsetting. The default ispause: the update stops at the problematic group of replicas, without touching the rest. The valuerollbackgoes further and automatically returns the service to the previous image version with no human intervention - exactly the option worth choosing for production services, where waiting for manual intervention costs more than an automatic rollback.