Blog / Company News / Building a Docker Swarm Cluster Across Multiple VPS Nodes

Building a Docker Swarm Cluster Across Multiple VPS Nodes

Set up a Docker Swarm cluster across several Contabo VPS instances — manager/worker setup through your first deployed stack.

11 min read

Docker Swarm turns several separate servers into one cluster that schedules containers across all of them. This docker swarm tutorial walks through a docker swarm setup with one manager node and two worker nodes. It also covers deploying a real stack across that cluster. The same steps work whether the nodes run on Contabo, a home lab, or a mix of providers, since Swarm only needs Docker Engine and network connectivity between the machines.

Docker Swarm Setup: Manager and Worker Nodes

A Docker Swarm cluster needs at least one manager node and, ideally, a couple of worker nodes to spread the load. The manager schedules containers and holds the cluster’s state. Workers simply run the containers they’re assigned, without making scheduling decisions of their own. A docker swarm setup follows a short, repeatable sequence:

  1. Provision three VPS instances running Ubuntu, each with Docker Engine already installed.
  2. Open the ports Swarm needs between the nodes: 2377 for cluster management, 7946 for node communication, and 4789 for overlay networking. Whitelist each node’s IP address specifically in the firewall rules, rather than opening these ports to the public internet.
  3. Run docker swarm init on the machine that becomes the manager.
  4. Copy the join token the command prints, then run it on each worker.
  5. Confirm every node appears in the cluster with docker node ls.

Choosing How Many Nodes to Start With

Three nodes make a practical starting point for testing this setup: one manager and two workers. That’s enough to see replicas scheduled across separate machines and to test what happens when a worker goes offline. Production clusters often add two more managers for quorum, plus additional workers to handle real traffic. The same commands apply at any size. Nothing about docker swarm init or the join process changes once the cluster grows beyond three nodes.

Initializing the Manager Node

Pick one VPS to act as the manager and run docker swarm init –advertise-addr <MANAGER-IP> on it, replacing <MANAGER-IP> with that server’s public or private IP address. This command creates the swarm and turns the local node into its first manager. Specifying the advertise address matters on servers with more than one network interface, since Swarm otherwise guesses which IP the other nodes should use. The command also prints a docker swarm join command containing a worker token, which the other nodes need. Save that output somewhere safe, since generating a new token later requires an extra command. Running docker swarm init a second time on the same machine returns an error, since a node can only belong to one swarm at a time.

Joining Worker Nodes

Run the join command from the previous step on each remaining VPS. It looks like this:

docker swarm join --token SWMTKN-1-xxxxxxxxxxxx <MANAGER-IP>:2377

Each worker connects to the manager over port 2377 and registers itself with the cluster. This step needs no extra configuration beyond a working Docker installation and open network ports between the nodes. Worker nodes don’t need any special privileges beyond that, since the manager handles all scheduling decisions on their behalf. If the original token expires or gets lost, run docker swarm join-token worker on the manager to print it again. A separate command, docker swarm join-token manager, generates a token for promoting a node to manager status instead.

Verifying the Cluster

Run docker node ls on the manager to confirm every node joined correctly. The output lists each node’s hostname, its role as either manager or worker, and its current availability. A healthy cluster shows every node marked Ready and Active. Worker nodes don’t show detailed cluster information by default, since only managers hold the full cluster state. That’s normal, and it’s part of what keeps workers lightweight. Re-running the command periodically is a simple way to catch a node that silently dropped out of the cluster.

Adding More Managers for Redundancy

A single manager works fine for testing, but it becomes a single point of failure in production. Promote an existing worker to manager with docker node promote <NODE-NAME>, run from an existing manager. Swarm recommends running managers in odd numbers, such as three or five, since it uses a quorum system to agree on the cluster’s state. With three managers, the cluster tolerates losing one without any disruption to scheduling. Adding a fourth manager doesn’t improve fault tolerance on its own, since quorum still requires a majority, so odd numbers make better use of each additional node.

Deploying a Stack Across the Swarm

Swarm deploys applications as stacks rather than through individual docker run commands. A stack file uses the same YAML format as a Compose file, with a few Swarm-specific additions like deploy blocks for replica counts and placement rules. This docker swarm tutorial builds on that format directly, since anyone who already writes Compose files needs only small changes to deploy the same application across a cluster instead of a single machine.

Writing a Stack File

Save the following as stack.yml on the manager node. The file extends the same web-and-database pattern from a single-host Compose setup, with deploy blocks added for each service:

version: "3.8"
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    deploy:
      replicas: 3
      restart_policy:
        condition: on-failure
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: examplepassword
    volumes:
      - db_data:/var/lib/postgresql/data
    deploy:
      placement:
        constraints:
          - node.role == manager
volumes:
  db_data:

The deploy block controls how Swarm schedules each service. The web service runs three replicas, spread automatically across available nodes based on current load. The db service, by contrast, pins itself to the manager node through a placement constraint, which keeps its volume on one predictable machine. Without that constraint, Swarm could schedule the database on a different worker each time it restarts, leaving it unable to find its previous data.

How Overlay Networking Connects Services

Swarm creates an overlay network automatically for every stack, letting services reach each other by name regardless of which physical node they run on. Keep all nodes in the same data center region when possible, since overlay network performance depends on latency between nodes. Nodes spread across distant regions still work, but the added latency slows down cross-node traffic noticeably. The web service can connect to the database using db as the hostname, exactly as it would in a single-host Compose setup. Traffic between replicas on different nodes travels over this overlay network, encrypted by default between nodes that support it. This is what makes port 4789 necessary between every node in the cluster, since that’s the port overlay traffic actually uses. Without it open, containers on different nodes can’t reach each other even though the stack deploys without errors. Double-checking this port early saves a confusing debugging session later, when everything appears to deploy correctly but services can’t actually talk to each other.

Deploying and Scaling the Stack

Deploy the stack with docker stack deploy -c stack.yml myapp, run from the manager. Swarm distributes the three web replicas across the cluster automatically, based on available resources. Swarm’s routing mesh also handles the incoming traffic on port 80, routing it to any healthy replica across the cluster automatically. This mesh prevents the port conflict that would otherwise happen when scaling a service outside Swarm. Swarm manages the published port at the cluster level instead of binding it directly inside each container, so multiple replicas can run on the same node without a conflict. Scale the service up or down later with docker service scale myapp_web=5, without touching the stack file at all. Swarm handles the rescheduling in the background and keeps the requested number of replicas running at all times. Add resource limits to the deploy block with a resources key. This prevents one service from starving the others on a shared node. Updating the image version and running docker stack deploy again triggers a rolling update, replacing old containers with new ones a few at a time instead of all at once.

Checking Service Status

Run docker stack services myapp to see how many replicas each service runs and how many are healthy. Use docker service ps myapp_web to see which specific nodes run each web replica. Check logs from a specific service with docker service logs myapp_web, which aggregates output from every replica into one stream. Remove the entire stack with docker stack rm myapp when it’s no longer needed. This tears down every service in the stack, though it leaves named volumes in place unless removed separately.

Why Run Docker Swarm on Unmanaged Contabo VPS Nodes

Each node in this cluster starts out as a plain Contabo VPS with nothing more than Ubuntu and Docker Engine installed. Nothing about the cluster comes preconfigured. Every manager and worker gets joined to the swarm by whoever builds the setup, using the same commands covered above. Contabo supplies the compute, storage, and network connection for each instance, but it doesn’t know these machines belong to a Swarm cluster at all. It plays no role in scheduling containers or managing the cluster’s state. None of this setup lives behind a proprietary dashboard specific to one hosting company.

That separation keeps the setup transparent. Nothing about how Swarm schedules containers, replicates services, or routes traffic between nodes depends on which hosting provider supplies the underlying servers. A cluster built from several VPS instances behaves identically to one built from any other set of nodes, as long as the network allows the required ports between them. Moving the cluster later, or adding a fourth node from a different provider entirely, works the same way. Install Docker, join the swarm, and let the scheduler take over from there.

Monitoring and recovery stay the operator’s job as well. If a worker node goes offline, Swarm reschedules its containers onto the remaining nodes automatically, but nothing restores that server itself. Bringing a failed node back into the cluster, or replacing it with a fresh instance, remains a manual step for whoever manages the cluster.

Certificate rotation and manager backups follow the same pattern. Swarm generates its own internal certificates for securing communication between nodes, and it rotates them automatically on a schedule. Backing up a manager’s swarm state, stored under /var/lib/docker/swarm, still falls to whoever runs the cluster, since the hosting provider has no visibility into what that directory contains. Losing every manager without a backup means rebuilding the cluster from scratch. Plan for that scenario before it happens, since Swarm has no way to reconstruct lost state on its own.

FAQ: Docker Swarm on a VPS

How do I set up Docker Swarm across multiple VPS?

A docker swarm setup across multiple VPS instances starts with Docker Engine installed on every machine. Run docker swarm init on the server chosen as manager, then run the join command it prints on each remaining server. Open ports 2377, 7946, and 4789 between all nodes beforehand, since Swarm can’t form a cluster without them. Confirm the setup with docker node ls, which should list every node as Ready. From there, deploying a stack works the same way regardless of how many nodes joined the cluster. Adding a node later needs nothing more than generating a fresh join token and running it on the new machine. Re-run docker node ls afterward to confirm the cluster still reports every member as healthy.

Is Docker Swarm easier than Kubernetes for a small cluster?

Generally, yes. Docker Swarm needs only a handful of commands to form a working cluster, while Kubernetes requires far more configuration even for a minimal setup. Swarm also reuses the same Compose file format most developers already know, which shortens the learning curve considerably. Kubernetes offers more advanced scheduling, scaling, and ecosystem tooling, which larger deployments eventually need. For a cluster of three to five VPS nodes running a handful of services, though, Swarm usually gets the job done with far less operational overhead. Teams that outgrow Swarm can migrate later, though the stack file format doesn’t carry over directly and needs rewriting for Kubernetes manifests.

Can I run Docker Swarm on a single Contabo VPS?

Yes. Running docker swarm init on one machine creates a single-node swarm, with that machine acting as both manager and worker. This setup won’t survive a hardware failure, since there’s no second node to take over, but it still gives access to stack files, service scaling commands, and overlay networking. This docker swarm tutorial focuses on a multi-node cluster because that’s where Swarm’s scheduling and redundancy features actually matter. Testing a stack file on a single node first, though, is a reasonable way to check the configuration before adding more machines to the cluster.

Share 𝕏 in