Native Docker clustering — turning a pool of Docker hosts into a single virtual system for high-availability, load balancing, and zero-downtime deployments.
Container orchestration automates the deployment, scaling, networking, and management of containerised applications across a cluster of machines.
Docker Swarm — built into Docker Engine, simple to set up | Kubernetes — industry standard, highly extensible | Nomad — HashiCorp, multi-workload scheduler
A Swarm cluster consists of manager nodes (control plane) and worker nodes (data plane). Managers use the Raft consensus algorithm to maintain a consistent cluster state.
Swarm mode is built into Docker Engine — no extra software needed. One command creates the cluster.
# Initialise the swarm on the first manager
docker swarm init --advertise-addr 192.168.1.10
# Output includes a join token for workers
# Swarm initialized: current node is now a manager
# Run on each worker node
docker swarm join \
--token SWMTKN-1-xxx...xxx \
192.168.1.10:2377
# Get the manager join token
docker swarm join-token manager
# Run on additional manager nodes
docker swarm join \
--token SWMTKN-1-yyy...yyy \
192.168.1.10:2377
# List all nodes
docker node ls
# ID HOSTNAME STATUS AVAILABILITY ROLE
# abc123 * manager1 Ready Active Leader
# def456 worker1 Ready Active
# ghi789 worker2 Ready Active
In Swarm, you deploy services (not individual containers). Each service spawns one or more tasks, and each task runs exactly one container.
# Create a replicated service
docker service create \
--name web \
--replicas 3 \
-p 80:80 \
nginx:alpine
# Create a global service
docker service create \
--name node-exporter \
--mode global \
prom/node-exporter
# List services
docker service ls
# See task placement
docker service ps web
# ID NAME NODE STATE
# a1b2c3 web.1 worker1 Running
# d4e5f6 web.2 worker2 Running
# g7h8i9 web.3 manager1 Running
# View service details
docker service inspect --pretty web
# View logs across all replicas
docker service logs web
New → Pending → Assigned → Accepted → Preparing → Starting → Running → Complete
Swarm has a built-in DNS server that automatically assigns each service a DNS entry. Containers can reach other services simply by name.
--endpoint-mode dnsrr# Services on the same overlay network
# can resolve each other by name
docker service create --name api \
--network backend \
myapp/api
docker service create --name db \
--network backend \
postgres:16
# Inside "api" container:
# ping db => resolves to VIP 10.0.1.5
# nslookup db => returns 10.0.1.5 (VIP)
# With DNS round-robin:
docker service create --name api \
--network backend \
--endpoint-mode dnsrr \
myapp/api
# nslookup api => returns all task IPs
Overlay networks create a distributed network across all Swarm nodes, enabling containers on different hosts to communicate as if they were on the same LAN.
--opt encrypted# Create an overlay network
docker network create \
--driver overlay \
--subnet 10.0.9.0/24 \
--opt encrypted \
my-overlay
# Attach services to the network
docker service create --name web \
--network my-overlay \
nginx:alpine
| Network | Purpose |
|---|---|
| ingress | Handles published port routing mesh |
| docker_gwbridge | Connects overlay to host network |
| user-defined overlay | Service-to-service communication |
Swarm provides two layers of load balancing: an external routing mesh (ingress) and internal VIP-based balancing.
--publish mode=host for direct host binding# Default: routing mesh
docker service create -p 80:80 web
# Host mode: bypass mesh
docker service create \
--publish mode=host,target=80,published=80 \
web
Swarm performs rolling updates by incrementally replacing tasks with the new version, ensuring zero downtime.
# Update image with rolling strategy
docker service update \
--image nginx:1.27 \
--update-parallelism 2 \
--update-delay 10s \
--update-failure-action rollback \
--update-max-failure-ratio 0.25 \
--update-order start-first \
web
# Rollback to the previous version
docker service rollback web
# Or configure auto-rollback
docker service create \
--name web \
--replicas 6 \
--update-failure-action rollback \
--rollback-parallelism 2 \
--rollback-delay 5s \
--rollback-max-failure-ratio 0.1 \
nginx:1.26
A stack is a group of related services defined in a Compose file and deployed to a Swarm cluster. Think of it as docker-compose for production.
# docker-stack.yml
version: "3.8"
services:
web:
image: myapp/web:2.1
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
ports:
- "80:80"
networks:
- frontend
- backend
api:
image: myapp/api:2.1
deploy:
replicas: 2
networks:
- backend
db:
image: postgres:16
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
networks:
frontend:
driver: overlay
backend:
driver: overlay
volumes:
db-data:
# Deploy a stack
docker stack deploy -c docker-stack.yml myapp
# List stacks
docker stack ls
# List services in a stack
docker stack services myapp
# List tasks in a stack
docker stack ps myapp
# Remove a stack
docker stack rm myapp
deploy: key is only used in Swarm modebuild: is ignored — stacks require pre-built imagesSwarm provides first-class secret management — secrets are encrypted at rest, transmitted only to nodes running tasks that need them, and mounted as in-memory files.
/run/secrets/# Create a secret
echo "s3cureP@ss" | docker secret create db_pass -
# Use in a service
docker service create --name api \
--secret db_pass \
myapp/api
# Inside container: cat /run/secrets/db_pass
# Rotate a secret
echo "newP@ss" | docker secret create db_pass_v2 -
docker service update \
--secret-rm db_pass \
--secret-add db_pass_v2 api
# Create a config
docker config create nginx_conf ./nginx.conf
# Use in a service
docker service create --name web \
--config source=nginx_conf,target=/etc/nginx/nginx.conf \
nginx:alpine
secrets:
db_password:
external: true # pre-created
api_key:
file: ./api_key.txt # from file
services:
api:
secrets:
- db_password
- api_key
Control where tasks are scheduled using constraints (hard rules) and preferences (soft rules / spread strategies).
# Add labels to nodes
docker node update --label-add zone=eu-west worker1
docker node update --label-add ssd=true worker2
# Constrain to specific nodes
docker service create --name db \
--constraint 'node.labels.ssd == true' \
--constraint 'node.role == worker' \
postgres:16
# Constrain to a specific hostname
docker service create --name monitoring \
--constraint 'node.hostname == manager1' \
grafana/grafana
# Spread replicas across availability zones
docker service create --name web \
--replicas 6 \
--placement-pref 'spread=node.labels.zone' \
nginx:alpine
# 2 tasks in zone=eu-west
# 2 tasks in zone=eu-central
# 2 tasks in zone=us-east
deploy:
placement:
constraints:
- node.labels.ssd == true
preferences:
- spread: node.labels.zone
Swarm continuously monitors task health. When a task becomes unhealthy or a node goes down, the orchestrator automatically reschedules tasks to maintain desired state.
# Define health check on service
docker service create --name web \
--health-cmd "curl -f http://localhost/ || exit 1" \
--health-interval 30s \
--health-timeout 10s \
--health-retries 3 \
--health-start-period 60s \
nginx:alpine
# Or in the Dockerfile
HEALTHCHECK --interval=30s --timeout=10s \
--retries=3 --start-period=60s \
CMD curl -f http://localhost/ || exit 1
deploy:
restart_policy:
condition: on-failure # none | on-failure | any
delay: 5s
max_attempts: 3
window: 120s
Scale services up or down with a single command. Swarm distributes new tasks across available nodes automatically.
# Scale a single service
docker service scale web=10
# Scale multiple services at once
docker service scale web=10 api=5 worker=8
# Scale down
docker service scale web=2
# Update replicas (alternative)
docker service update --replicas 10 web
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
docker service ps to verify distributionEffective monitoring covers cluster health, service metrics, and container-level resource usage.
# Cluster overview
docker node ls
docker service ls
# Task health and placement
docker service ps web --no-trunc
# Container stats (per node)
docker stats
# System-wide info
docker system df
docker info
services:
prometheus:
image: prom/prometheus
deploy:
placement:
constraints: [node.role == manager]
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports: ["9090:9090"]
grafana:
image: grafana/grafana
ports: ["3000:3000"]
cadvisor:
image: gcr.io/cadvisor/cadvisor
deploy:
mode: global # one per node
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
node-exporter:
image: prom/node-exporter
deploy:
mode: global
| Aspect | Docker Swarm | Kubernetes |
|---|---|---|
| Setup complexity | One command (docker swarm init) | Multiple components (API server, etcd, kubelet, etc.) |
| Learning curve | Gentle — extends familiar Docker CLI | Steep — new concepts (Pods, Deployments, Ingress...) |
| Auto-scaling | Manual or external tools | Built-in HPA/VPA, KEDA |
| Networking | Simple overlay + routing mesh | CNI plugins (Calico, Cilium, Flannel...) |
| Service mesh | Not built-in | Istio, Linkerd, Cilium mesh |
| Storage | Docker volumes, NFS plugins | CSI drivers, PVs/PVCs, StorageClasses |
| Ecosystem | Smaller, Docker-focused | Massive (Helm, Operators, CRDs, GitOps...) |
| Production adoption | Niche / small-medium workloads | Industry standard, all cloud providers |
| Configuration | Compose YAML (familiar) | K8s manifests (verbose) |
| Community | Smaller, less active | Huge, CNCF backed |
Start with Swarm to learn orchestration concepts (services, replicas, rolling updates, overlay networks). These concepts transfer directly to Kubernetes. Many teams start with Swarm and migrate to K8s when they outgrow it.
docker swarm init — create a clusterdocker service create — deploy a servicedocker service scale — scale replicasdocker service update — rolling updatedocker stack deploy — deploy from Composedocker node ls — inspect cluster