Skip to main content

Docker Swarm — Container Orchestration Without Kubernetes Complexity

· 8 min read
Goel Academy
DevOps & Cloud Learning Hub

You have containers running on a single host. Now you need them running across five hosts with load balancing, rolling updates, and automatic restarts. Kubernetes is one answer, but it comes with a steep learning curve and significant operational overhead. Docker Swarm is built into the Docker Engine, requires no additional installation, and can have a production cluster running in under ten minutes.

Swarm vs Kubernetes

Before investing time in either, here is an honest comparison.

FeatureDocker SwarmKubernetes
Setup complexityOne command (docker swarm init)Multi-component install (API server, etcd, scheduler, kubelet)
Learning curveLow — uses Docker CLI and Compose filesSteep — new concepts (pods, deployments, services, ingress)
ScalingGood for small-medium clusters (dozens of nodes)Designed for massive scale (thousands of nodes)
Auto-scalingManual scaling onlyHorizontal Pod Autoscaler
Service discoveryBuilt-in DNSCoreDNS + Services
Load balancingBuilt-in routing meshRequires ingress controller
Rolling updatesBuilt-in with configurable parallelismBuilt-in with advanced strategies (canary, blue-green)
Secret managementBuilt-in encrypted secretsBuilt-in with encryption at rest
EcosystemDocker-native toolsMassive ecosystem (Helm, Istio, ArgoCD, etc.)
Best forSmall teams, simple deploymentsLarge teams, complex microservices

If you have fewer than 50 services and a small team, Swarm can handle everything you need with a fraction of the complexity.

Initializing a Swarm

Every Swarm cluster starts with one manager node.

# Initialize Swarm on the first node
docker swarm init --advertise-addr 192.168.1.10

# Output:
# Swarm initialized: current node (abc123) is now a manager.
#
# To add a worker to this swarm, run the following command:
# docker swarm join --token SWMTKN-1-abc123...xyz789 192.168.1.10:2377
#
# To add a manager to this swarm, run:
# docker swarm join-token manager
# Get the join tokens at any time
docker swarm join-token worker # Token for worker nodes
docker swarm join-token manager # Token for manager nodes

# Rotate tokens (invalidate old ones) for security
docker swarm join-token --rotate worker

Joining Nodes

On each worker machine, paste the join command from the init output.

# On worker nodes — join the swarm
docker swarm join --token SWMTKN-1-abc123...xyz789 192.168.1.10:2377

# On a node you want as an additional manager
docker swarm join --token SWMTKN-1-mgr456...def012 192.168.1.10:2377
# Back on the manager — verify the cluster
docker node ls

# ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
# abc123 * manager-1 Ready Active Leader 24.0.7
# def456 worker-1 Ready Active 24.0.7
# ghi789 worker-2 Ready Active 24.0.7

# Promote a worker to manager (for HA — use odd numbers: 3 or 5 managers)
docker node promote worker-1

# Demote a manager back to worker
docker node demote worker-1

For high availability, run 3 or 5 manager nodes. Swarm uses the Raft consensus protocol, so it tolerates (N-1)/2 manager failures. Three managers tolerate one failure. Five tolerate two.

Deploying Services

A service is Swarm's unit of deployment — like a long-running container with built-in replication and restart.

# Create a simple service
docker service create --name web \
--replicas 3 \
--publish 80:80 \
nginx:alpine

# List services
docker service ls
# ID NAME MODE REPLICAS IMAGE PORTS
# abc123 web replicated 3/3 nginx:alpine *:80->80/tcp

# See where tasks are running
docker service ps web
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# xyz1 web.1 nginx:alpine manager-1 Running Running 2 minutes ago
# xyz2 web.2 nginx:alpine worker-1 Running Running 2 minutes ago
# xyz3 web.3 nginx:alpine worker-2 Running Running 2 minutes ago

Scaling Services

# Scale up to 10 replicas
docker service scale web=10

# Scale multiple services at once
docker service scale web=10 api=5 worker=3

# Check the scaling progress
docker service ls
# NAME MODE REPLICAS IMAGE
# web replicated 10/10 nginx:alpine
# api replicated 5/5 myapp:latest
# worker replicated 3/3 myworker:latest

If a node goes down, Swarm automatically reschedules the tasks on the remaining healthy nodes.

Rolling Updates

When you push a new image version, Swarm updates containers one batch at a time.

# Update the image with a rolling update
docker service update --image nginx:1.25-alpine web

# Configure update behavior
docker service update \
--image myapp:v2 \
--update-parallelism 2 \
--update-delay 10s \
--update-failure-action rollback \
--update-max-failure-ratio 0.25 \
api

# Watch the update progress
docker service ps api
Update FlagPurposeDefault
--update-parallelismHow many tasks to update at once1
--update-delayWait time between batches0s
--update-failure-actionWhat to do on failure (pause, rollback, continue)pause
--update-max-failure-ratioMax percentage of failed tasks before triggering failure action0
--update-orderstart-first (start new before stopping old) or stop-firststop-first
# Rollback to the previous version manually
docker service rollback api

Overlay Networks

Swarm uses overlay networks to connect containers across multiple hosts.

# Create an overlay network
docker network create --driver overlay app-network

# Deploy services on the same overlay network
docker service create --name api \
--network app-network \
--replicas 3 \
myapp:latest

docker service create --name db \
--network app-network \
--replicas 1 \
postgres:16-alpine

# The API containers can reach the database at "db:5432"
# regardless of which physical host they are on

Services on the same overlay network get automatic DNS-based service discovery. The service name resolves to a virtual IP that load-balances across all healthy replicas.

Swarm Routing Mesh

The routing mesh is one of Swarm's best features. When you publish a port, that port becomes available on every node in the swarm — even nodes not running the service.

# Publish port 80 — accessible on ALL nodes
docker service create --name web \
--publish 80:80 \
--replicas 3 \
nginx:alpine

# Any of these URLs will work, even if that node has zero replicas:
# http://manager-1:80
# http://worker-1:80
# http://worker-2:80

Put an external load balancer (HAProxy, AWS ALB, Nginx) in front of your swarm nodes, point it at all nodes on port 80, and Swarm handles the internal routing.

Secrets Management

Swarm has built-in encrypted secret management. Secrets are encrypted at rest and only mounted in memory inside containers that need them.

# Create a secret from a file
echo "my_db_password" | docker secret create db_password -

# Create a secret from a file
docker secret create tls_cert ./server.crt

# List secrets
docker secret ls

# Use secrets in a service
docker service create --name api \
--secret db_password \
--secret tls_cert \
myapp:latest

# Inside the container, secrets appear as files in /run/secrets/
# /run/secrets/db_password → contains "my_db_password"
# /run/secrets/tls_cert → contains the certificate content
# Rotate a secret (create new, update service, remove old)
echo "new_password" | docker secret create db_password_v2 -

docker service update \
--secret-rm db_password \
--secret-add db_password_v2 \
api

docker secret rm db_password

Stack Deploy with Compose Files

The real power of Swarm comes from deploying entire application stacks using Compose files.

# docker-compose.yml (Swarm stack)
version: "3.8"

services:
web:
image: nginx:alpine
ports:
- "80:80"
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
networks:
- frontend

api:
image: myapp:latest
deploy:
replicas: 5
resources:
limits:
cpus: "1.0"
memory: 512M
secrets:
- db_password
networks:
- frontend
- backend

db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
secrets:
- db_password
deploy:
placement:
constraints:
- node.role == manager
networks:
- backend

networks:
frontend:
backend:

volumes:
pgdata:

secrets:
db_password:
external: true
# Deploy the stack
docker stack deploy -c docker-compose.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 the stack
docker stack rm myapp

When Swarm Is the Right Choice

Choose Swarm when:

  • You need multi-host orchestration but not Kubernetes-level complexity.
  • Your team already knows Docker and Docker Compose.
  • You have fewer than 50 services and fewer than 20 nodes.
  • You want built-in load balancing and service discovery without configuring ingress controllers.
  • You need a quick path from single-host Docker Compose to multi-host deployment.

Choose Kubernetes when:

  • You need auto-scaling, advanced deployment strategies (canary, blue-green), or a service mesh.
  • You have a large team with dedicated platform engineers.
  • You need the ecosystem — Helm charts, operators, GitOps tools.
  • You are running hundreds of services across many clusters.

Wrapping Up

Docker Swarm is container orchestration for people who want to ship, not spend weeks learning a new platform. It reuses the Docker CLI you already know, works with Compose files you already have, and provides production-grade features — rolling updates, secret management, overlay networking, and built-in load balancing — without a single YAML indentation headache involving pod specs and sidecar containers.

In the next post, we will cover BuildKit — Docker's next-generation build engine that brings parallelized builds, cache mounts, secret handling during builds, and multi-platform image support.