Docker Swarm · guide
Docker Swarm Guide
Swarm mode, built into Docker Engine, manages a cluster of Docker hosts as one swarm. You declare services and replicas; the swarm decides which node runs what. See the official Swarm docs.
Last updated
On this page · 13 sections
1. What Is Docker Swarm
Docker Swarm is Docker's built-in container orchestration. It is included in every Docker Engine installation — no extra software to install. Swarm is a good choice when you need multi-node clustering without the complexity of Kubernetes.
Key concepts:
- Manager nodes handle scheduling, state management, and the Raft consensus.
- Worker nodes run the actual container workloads. (Managers also run tasks by default unless you drain them.)
- Services are the unit of deployment (like a Compose service, but distributed).
- Tasks are individual containers running as part of a service.
2. Initialize a Swarm
Run this on the machine that will be the first manager. This enables swarm mode and makes the current node a manager.
docker swarm init
If the machine has multiple network interfaces, specify the advertise address:
docker swarm init --advertise-addr 192.168.1.10
After init, Docker prints a docker swarm join command with a token. Save it to add worker nodes.
# View the join token again later
docker swarm join-token worker
docker swarm join-token manager
3. Adding Worker and Manager Nodes
Run the join command on each additional machine. The token determines whether it joins as a worker or manager.
Add a worker node:
# Run on the machine you want to add as a worker
docker swarm join --token SWMTKN-1-xxxxx 192.168.1.10:2377
Add a manager node:
# Get the manager join token (run on existing manager)
docker swarm join-token manager
# Then run the printed command on the new manager node
docker swarm join --token SWMTKN-1-yyyyy 192.168.1.10:2377
# List all nodes in the swarm (run on a manager)
docker node ls
# Promote a worker to manager
docker node promote <node-name>
# Demote a manager to worker
docker node demote <node-name>
# Remove a node (run on manager) — it must have left the swarm or be down first;
# --force is for a node you can no longer reach
docker node rm <node-name>
# Leave the swarm (run on the node itself)
docker swarm leave
# For a manager, demote it first (docker node demote <node-name>), then run
# docker swarm leave without --force. --force skips the quorum safeguard: only use it
# when the swarm will not be used again, e.g. a single-node swarm
docker swarm leave --force
For high availability, use 3 or 5 manager nodes (always an odd number for Raft consensus).
4. Creating Services
A service is how you deploy a container to the swarm. The swarm scheduler decides which node(s) run it.
# Create a simple service with 1 replica
docker service create --name web --publish 80:80 nginx:alpine
# Create a service with 3 replicas
docker service create --name api --replicas 3 --publish 4000:4000 my-api:1.0
# Create a service with environment variables
docker service create --name db \
--env POSTGRES_USER=app \
--env POSTGRES_PASSWORD=secret \
--mount type=volume,source=pgdata,target=/var/lib/postgresql/data \
postgres:16-alpine
# List all services
docker service ls
# Show detailed info about a service
docker service inspect --pretty web
# Show which nodes are running a service's tasks
docker service ps web
5. Scaling Services
Scale a service up or down by changing the replica count. The swarm distributes tasks across available nodes.
# Scale to 5 replicas
docker service scale web=5
# Scale multiple services at once
docker service scale web=5 api=3
# Verify the scaling
docker service ls
docker service ps web
To scale to zero (stop all tasks without removing the service):
docker service scale web=0
6. Rolling Updates and Rollbacks
Swarm performs rolling updates by default — replacing tasks one (or a few) at a time so the service stays available.
# Update the image for a service
docker service update --image nginx:1.30-alpine web
# Control update behavior
docker service update \
--image my-api:2.0 \
--update-parallelism 2 \
--update-delay 10s \
--update-failure-action rollback \
--update-order start-first \
api
Update options explained:
--update-parallelism 2— update 2 tasks at a time.--update-delay 10s— wait 10 seconds between batches.--update-failure-action rollback— auto-rollback if the update fails.--update-order start-first— start the new task before stopping the old one (zero-downtime).
# Manually rollback to the previous version
docker service rollback web
# Check update/rollback status
docker service inspect --pretty web
7. Stack Deploys with Compose Files
A stack is a group of services deployed from a Compose file. This is the recommended way to deploy multi-service apps to Swarm.
# stack.yml
services:
web:
image: nginx:alpine
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
ports:
- "80:80"
api:
image: my-api:1.0
deploy:
replicas: 2
environment:
DATABASE_URL: postgres://app:secret@db:5432/appdb
db:
image: postgres:16-alpine
deploy:
replicas: 1
placement:
constraints:
# Pin to ONE node: named volumes are local to a node, so a task that
# moved to another manager would start with an empty pgdata.
# Label it first: docker node update --label-add db=true <node>
- node.labels.db == true
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
pgdata:
# Deploy a stack
docker stack deploy -c 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
Note: docker stack deploy ignores build: (it prints "Ignoring unsupported options: build"), so all images must be pre-built and available (either on a registry or locally on every node). It also reads the legacy Compose file format 3, not the current Compose Specification, so some keys that work with docker compose are not supported in a stack file.
8. Overlay Networks
Overlay networks span multiple nodes, allowing containers on different hosts to communicate. Application traffic on an overlay is not encrypted unless you create the network with --opt encrypted (IPsec; it has a performance cost and is not supported for Windows containers).
# Create an overlay network
docker network create --driver overlay my-overlay
# Create an attachable overlay (allows standalone containers to join too)
docker network create --driver overlay --attachable my-overlay
# Create a service on the overlay network
docker service create --name api --network my-overlay my-api:1.0
docker service create --name db --network my-overlay --env POSTGRES_PASSWORD=change-me postgres:16-alpine
In a stack file, networks are created automatically:
services:
web:
image: nginx:alpine
networks:
- frontend
api:
image: my-api:1.0
networks:
- frontend
- backend
db:
image: postgres:16-alpine
networks:
- backend
networks:
frontend:
driver: overlay
backend:
driver: overlay
Swarm provides built-in DNS so services find each other by name. The api service can reach db at hostname db on port 5432.
9. Secrets and Configs
Swarm has built-in secret management. Secrets are encrypted at rest and only sent to nodes that need them.
# Create a secret from stdin (printf, not echo, so no trailing newline ends up in the
# secret; the value is left in your shell history)
printf "supersecretpassword" | docker secret create db_password -
# Create a secret from a file
docker secret create tls_cert ./server.crt
# List secrets
docker secret ls
# Use a secret in a service (mounted at /run/secrets/<name>)
docker service create --name db \
--secret db_password \
--env POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
postgres:16-alpine
Configs work like secrets but for non-sensitive configuration files:
# Create a config
docker config create nginx_conf ./nginx.conf
# Use it in a service
docker service create --name web \
--config source=nginx_conf,target=/etc/nginx/nginx.conf \
nginx:alpine
In a stack file:
services:
db:
image: postgres:16-alpine
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
external: true # secret must be created before deploying
10. Placement Constraints and Labels
Control which nodes run which services using placement constraints and labels.
# Add a label to a node
docker node update --label-add disk=ssd node-03
# Create a service constrained to manager nodes
docker service create --name monitoring \
--constraint 'node.role == manager' \
prom/prometheus:v3.15.0
# Create a service constrained to nodes with a label
docker service create --name db \
--constraint 'node.labels.disk == ssd' \
--env POSTGRES_PASSWORD=change-me \
postgres:16-alpine
In a stack file:
services:
db:
image: postgres:16-alpine
deploy:
placement:
constraints:
- node.role == manager
- node.labels.disk == ssd
replicas: 1
worker:
image: my-worker:1.0
deploy:
placement:
preferences:
- spread: node.labels.zone
replicas: 6
The spread preference distributes replicas evenly across the specified label values (e.g., availability zones).
11. Resource Limits and Reservations
# Ceilings: the container is killed if it exceeds the memory limit
docker service create --name web-limited \
--limit-memory 512m --limit-cpu 1.5 nginx:alpine
# Floors: what each task needs before the scheduler will place it
docker service create --name web-reserved \
--reserve-memory 256m --reserve-cpu 0.5 nginx:alpine
# Change them on a running service
docker service update --limit-memory 1g web
⚠️ Reservations are scheduling constraints, and this is the one that surprises
people: if no node has the reserved amount free, the task is not placed and
the service sits in Pending indefinitely. Nothing has failed, so
nothing restarts — the scheduler is still waiting for somewhere to put it, and the
task's ERROR column says so: no suitable node (insufficient resources on N nodes).
# Tasks stuck Pending, with the reason in the error column
docker service ps web --no-trunc
# What each node has to offer
docker node inspect <node> --format '{{.Description.Resources.NanoCPUs}} {{.Description.Resources.MemoryBytes}}'
💡 Reserve what a task needs, not what it peaks at. Reservations are subtracted from a node's advertised capacity whether or not the task is using them, so generous reservations quietly shrink the cluster.
12. Monitoring and Troubleshooting
Use these commands to inspect the state of your swarm and debug issues.
# List all services and their replica counts
docker service ls
# Show tasks for a service (which node, status, errors)
docker service ps web
# Full task IDs and untruncated error messages (failed and shut-down tasks are
# listed by default; add --filter desired-state=running to hide them)
docker service ps web --no-trunc
# View logs for a service (aggregated from all replicas)
docker service logs -f web
# View logs for a specific task
docker service logs <task-id>
# Inspect a service for full config details
docker service inspect --pretty web
# Inspect a node
docker node inspect --pretty <node-name>
# Check node availability and drain a node for maintenance
docker node update --availability drain <node-name>
# Bring it back
docker node update --availability active <node-name>
Common issues:
- Service stuck at 0/N replicas: Check
docker service ps <name>for error messages. Common causes: image not found (not pushed to registry), port conflicts, resource constraints. - Tasks in "rejected" state: The worker node rejected the task — commonly because the image could not be pulled (
No such imagein the error column). Unmet placement constraints or resource reservations do not reject a task; they leave it Pending. - Nodes showing "down": Network partition or Docker stopped on that node. Check connectivity and
systemctl status docker.
13. Firewall Ports
For swarm nodes to communicate, the following ports must be open between the nodes — and only between them. Port 4789 carries VXLAN, which has no authentication: never open it to the internet or at a perimeter firewall. If you use encrypted overlay networks (--opt encrypted), also allow IP protocol 50 (ESP).
| Port | Protocol | Purpose |
|---|---|---|
| 2377 | TCP | Cluster management and Raft consensus |
| 7946 | TCP + UDP | Node discovery and gossip protocol |
| 4789 | UDP | VXLAN overlay network traffic |
# Replace 192.168.1.0/24 with the subnet your swarm nodes use
# UFW (Ubuntu)
sudo ufw allow from 192.168.1.0/24 to any port 2377 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 7946 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 7946 proto udp
sudo ufw allow from 192.168.1.0/24 to any port 4789 proto udp
# firewalld (CentOS/RHEL) — a zone that applies only to traffic from the node subnet
sudo firewall-cmd --permanent --new-zone=swarm
sudo firewall-cmd --permanent --zone=swarm --add-source=192.168.1.0/24
sudo firewall-cmd --permanent --zone=swarm --add-port=2377/tcp
sudo firewall-cmd --permanent --zone=swarm --add-port=7946/tcp
sudo firewall-cmd --permanent --zone=swarm --add-port=7946/udp
sudo firewall-cmd --permanent --zone=swarm --add-port=4789/udp
sudo firewall-cmd --reload
# iptables
sudo iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 2377 -j ACCEPT
sudo iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 7946 -j ACCEPT
sudo iptables -A INPUT -s 192.168.1.0/24 -p udp --dport 7946 -j ACCEPT
sudo iptables -A INPUT -s 192.168.1.0/24 -p udp --dport 4789 -j ACCEPT
If you use cloud provider security groups (AWS, GCP, etc.), add these rules to allow traffic between your swarm nodes.
Terms used here (1)plain definitions
Short definitions for the vocabulary on this page, in case any of it is new. Each links to the full entry.
- Docker Swarm
- Docker Engine's built-in orchestration mode (swarm mode), which joins Docker hosts into a cluster of managers and workers and keeps each service at the number of replicas you declare. Swarm provides service discovery, load balancing, and rolling updates with a simpler learning curve than Kubernetes.
Where to go next
SwarmBot
Answers from the Docker Swarm guide