Container orchestration transforms a single VPS into the foundation of a highly available application cluster. Docker Swarm, included natively with Docker, provides orchestration with minimal overhead and no additional licensing costs. This guide walks through setting up a three-node Docker Swarm cluster across multiple VPS instances for production-grade high availability. For VPS plans with enough resources to run a Swarm cluster, check our provider comparison.
Prerequisites
- Three VPS instances (1 vCPU, 2 GB RAM minimum each)
- Ubuntu 22.04 or 24.04 LTS on all nodes
- Private networking enabled between nodes (same provider VLAN or VPN)
- Open ports: 2377/tcp (cluster management), 7946/tcp+udp (node communication), 4789/udp (overlay network)
Step 1: Install Docker on All Nodes
Run the following on every VPS in the cluster:
# Remove old packages
sudo apt remove -y docker docker-engine docker.io containerd runc
# Install dependencies
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# Add Docker GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Verify installation
sudo docker --version
sudo docker run hello-world
Step 2: Initialize the Swarm Cluster
Choose one node as the manager (the first node that initializes the cluster):
# On manager node (use private IP if available)
sudo docker swarm init --advertise-addr 10.0.0.1
# Output contains the join token for worker nodes
# Copy the full output - it looks like:
# docker swarm join --token SWMTKN-1-xxxxx 10.0.0.1:2377
To add manager redundancy, promote additional manager nodes. Docker Swarm recommends 3 or 5 managers for production:
# On manager node, get the manager join token
sudo docker swarm join-token manager
# On the second manager node, run the join command from above output
sudo docker swarm join --token SWMTKN-1-xxxxx 10.0.0.1:2377
Step 3: Add Worker Nodes
# On manager, get the worker join token
sudo docker swarm join-token worker
# On each worker VPS
sudo docker swarm join --token SWMTKN-1-xxxxx 10.0.0.1:2377
Step 4: Verify the Cluster
# On any manager node
sudo docker node ls
# Expected output:
# ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
# abc... * node-1 Ready Active Reachable 24.0.7
# def... node-2 Ready Active Leader 24.0.7
# ghi... node-3 Ready Active 24.0.7
Step 5: Deploy a Highly Available Service
Deploy an Nginx service with 3 replicas distributed across the cluster:
# Create an overlay network for the service
sudo docker network create --driver overlay --attachable app-network
# Deploy the service with 3 replicas
sudo docker service create --name web --replicas 3 --network app-network --publish published=80,target=80 nginx:alpine
# Verify the service
sudo docker service ls
sudo docker service ps web
Step 6: Test High Availability
Simulate a node failure and verify automatic rescheduling:
# On the manager, drain a worker node
sudo docker node update --availability drain node-3
# Check that Swarm rescheduled the containers to other nodes
sudo docker service ps web
# Bring the node back
sudo docker node update --availability active node-3
Step 7: Rolling Updates and Rollbacks
Swarm handles zero-downtime deployments natively:
# Update the image with rolling update (updates 1 replica at a time)
sudo docker service update --image nginx:1.25 --update-parallelism 1 --update-delay 10s web
# Monitor the update
sudo docker service ps web
# Rollback if something goes wrong
sudo docker service rollback web
Step 8: Add a Load Balancer (Traefik)
For production traffic, use Traefik as a reverse proxy that auto-discovers Swarm services:
# Deploy Traefik as a global service
sudo docker service create --name traefik --constraint 'node.role == manager' --publish published=443,target=443 --publish published=80,target=80 --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock --network app-network traefik:v3.0 --api.insecure=false --providers.docker=true --providers.docker.swarmmode=true --entrypoints.websecure.address=:443 --entrypoints.web.address=:80
Step 9: Persistent Storage with NFS
Stateful services need shared storage. Mount an NFS share on all nodes:
# On all nodes (assuming an NFS server at 10.0.0.100)
sudo apt install -y nfs-common
sudo mkdir -p /mnt/shared
sudo mount -t nfs 10.0.0.100:/exports/shared /mnt/shared
# Add to /etc/fstab for persistence
echo "10.0.0.100:/exports/shared /mnt/shared nfs defaults 0 0" | sudo tee -a /etc/fstab
Step 10: Monitoring the Swarm
# Check node resource usage across the cluster
sudo docker node ps $(sudo docker node ls -q)
# View service logs
sudo docker service logs --tail 50 web
# Real-time container stats across the cluster
sudo docker stats --no-stream
Conclusion
Docker Swarm turns multiple VPS instances into a unified, highly available application platform with minimal overhead. Unlike Kubernetes, Swarm is simpler to set up and manage on small clusters (3–10 nodes), making it an excellent choice for VPS-based production deployments. With rolling updates, automatic failover, and built-in load balancing, your applications stay online even when individual nodes fail. Start with 3 manager nodes for a fault-tolerant control plane, then add worker nodes as your workload grows.




Leave a Reply
You must be logged in to post a comment.