Services, replicas & scaling
The declarative unit of Swarm.
Set a thermostat to 21 degrees and it stops caring about the details. It never asks you which vents should open or how long the furnace should run. It keeps nudging the room toward the number you picked, and it does that forever. A Docker service works the same way. You declare an image (the packaged filesystem a container starts from) and how many copies you want, and the swarm (a group of Docker machines pooled together so they behave like one big machine) keeps reality matching that number. docker service create is the swarm's version of docker run. Instead of starting one container on the machine sitting in front of you, it hands the whole cluster a goal and lets the cluster decide which machines the copies land on. Each machine in the cluster is called a node, and two modes cover nearly everything you will ever deploy: replicated keeps a fixed count of copies, and global runs exactly one copy on every node.
$ docker service create --name web --replicas 3 -p 80:80 nginx:1.27z1y2x3w4v5u6t7s8r9q0p1o2moverall progress: 3 out of 3 tasksverify: Service converged$ docker service lsID NAME MODE REPLICAS IMAGE PORTSz1y2x3w4v5u6 web replicated 3/3 nginx:1.27 *:80->80/tcp$ docker service ps webID NAME IMAGE NODE DESIRED STATE CURRENT STATE7a1b2c web.1 nginx:1.27 node-1 Running Running 2 minutes ago8d3e4f web.2 nginx:1.27 node-2 Running Running 2 minutes ago9g5h6i web.3 nginx:1.27 node-1 Running Running 2 minutes ago
Look at the REPLICAS column: 3/3. Read it like a score, running over desired. You asked for three and three are up. That fraction is the one number to keep your eye on. Desired is the goal a manager node (the node that makes scheduling decisions for the cluster) wrote down. Running is how many tasks are actually alive this second. A task is the swarm's word for one container plus the standing job of keeping that container alive. The names web.1, web.2 and web.3 are slots, and a slot holds exactly one task at a time. When the two numbers match, the service has converged and the cluster has nothing left to do. When they differ, the cluster is mid-repair, and it keeps placing tasks until they meet.
When running falls behind desired
A node reboots for a kernel patch. Every task it was running dies with it. A manager notices within seconds. Desired still reads three, running has dropped to two, and the scheduler (the part of the manager that decides which node runs what) starts a replacement on a node that is still up. You type nothing. You can trigger that same repair deliberately by draining a node, which is also the polite way to pull a machine out for maintenance instead of dropping its work on the floor.
$ docker node update --availability drain node-2node-2$ docker service lsID NAME MODE REPLICAS IMAGE PORTSz1y2x3w4v5u6 web replicated 2/3 nginx:1.27 *:80->80/tcp$ docker service ps webID NAME IMAGE NODE DESIRED STATE CURRENT STATEa1b2c3 web.2 nginx:1.27 node-3 Running Running 4 seconds agod4e5f6 \_ web.2 nginx:1.27 node-2 Shutdown Shutdown 5 seconds ago7a1b2c web.1 nginx:1.27 node-1 Running Running 6 minutes ago9g5h6i web.3 nginx:1.27 node-1 Running Running 6 minutes ago$ docker service ls # a few seconds laterID NAME MODE REPLICAS IMAGE PORTSz1y2x3w4v5u6 web replicated 3/3 nginx:1.27 *:80->80/tcp
That second web.2 line, the one with the \_ in front of it, is the slot's history. The old task shut down on node-2 and a fresh task for the same slot came up on node-3. The slot name never changed. The container underneath it got swapped. That is the entire mental model for Swarm. A manager runs one loop: compare desired against running, act on the gap, repeat, and never stop. Scaling, rolling updates, a dead node, a command you typed at 2am and regretted by 2:01, they all get settled by that single loop. Once it clicks, nothing the cluster does will surprise you again.
Scaling means moving one number
Traffic climbs and three copies stop being enough. Scaling is one command. You raise the desired count, the scheduler places the new tasks on nodes that still have room, and the swarm's built-in load balancer starts spreading incoming requests across all of them. When the rush fades, set the number back down and the extra tasks stop. You never log into each host, and you never pick which node gets which copy. You move one number and the cluster catches up. The count at the end of the last command shows where the ten copies actually landed, which was the scheduler's decision, not yours.
$ docker service scale web=10web scaled to 10overall progress: 10 out of 10 tasksverify: Service converged$ docker service lsID NAME MODE REPLICAS IMAGE PORTSz1y2x3w4v5u6 web replicated 10/10 nginx:1.27 *:80->80/tcp$ docker service ps web --filter desired-state=running --format '{{.Node}}' | sort | uniq -c4 node-13 node-23 node-3
Rolling out a new version without a gap
Shipping a new image should not take the site down, and neither should shipping a broken one. A service update swaps tasks a few at a time rather than all at once, the way a road crew closes a single lane instead of the whole motorway. You choose how many replicas to swap per step and how long to wait between steps, so a bad version has time to show up on your dashboards before it reaches every copy. Then set the failure action to rollback. If a new task crashes or fails its health check, the rollout stops and the service returns to its previous spec, meaning the saved description of what the service should be: image, ports, replica count and the rest. The damage stays inside one step instead of walking across all three replicas. docker service rollback does the same thing on demand, after the fact, when you decide the new version is wrong for reasons no health check caught.
$ docker service update \--image registry.internal/web:1.28 \--update-parallelism 1 --update-delay 15s \--update-failure-action rollback \webweboverall progress: 3 out of 3 tasksverify: Service converged$ docker service ps web --format '{{.Name}}\t{{.Image}}\t{{.CurrentState}}'web.1 registry.internal/web:1.28 Running about a minute agoweb.2 registry.internal/web:1.28 Running about a minute agoweb.3 registry.internal/web:1.28 Running about a minute ago$ docker service rollback web # revert to the previous spec on demandweboverall progress: 3 out of 3 tasksverify: Service converged
Some workloads belong on every host rather than in a fixed count. A log shipper. A metrics agent. A security sensor watching for odd process activity. Each of those should already be running on every node, and it should start on its own the day you add another machine next month. That is global mode. The swarm places exactly one task per node and keeps that promise as the cluster grows or shrinks. You cannot scale a global service, because "one on every node" has already answered the question of how many. For a security sensor the difference is the whole point: a replicated agent leaves you blind on whichever hosts it happened to skip, and blind spots are where attackers like to sit.
$ docker service create --mode global --name node-agent \--mount type=bind,source=/var/log,target=/var/log,readonly \registry.internal/log-agent:1.0x9y8z7w6v5u4t3s2r1q0overall progress: 3 out of 3 tasks (3 nodes)verify: Service converged$ docker service ps node-agent --format '{{.Name}}\t{{.Node}}\t{{.CurrentState}}'node-agent.k3n1 node-1 Running 30 seconds agonode-agent.k3n2 node-2 Running 30 seconds agonode-agent.k3n3 node-3 Running 30 seconds ago
Desired state, checked over and over
A service spec lists the image, the published ports, the networks it joins, the secrets it needs and the replica count. The swarm schedules tasks onto nodes from that spec and replaces failed ones until running matches desired again. Scaling is an edit to the count. Rollback is an edit back to an older version of the same spec. Both are the same operation wearing different clothes. Nobody places containers by hand on individual hosts.
Placement constraints, restart policies and update parallelism decide your blast radius on deploy day. Many replicas buy availability, cost more money and make draining connections cleanly harder. Few replicas are cheap and fragile. Always set resource reservations, a floor of CPU and memory the scheduler has to find before it will place a task, so it stops stacking work onto a node until the kernel starts killing processes for running out of memory.
When something looks wrong, read the tasks rather than the service name. A service can show a calm-looking line while sitting at 0/3 because the image will not pull. docker service ps is where the truth lives.
Before you scale or update a service that real users depend on, write three things into the ticket: the replica count and image tag the service had before you touched it, the host you ran the command from, and the exact command that puts it back. For a service that undo is usually docker service rollback plus the old tag, and it belongs in the ticket rather than in somebody's shell history. Paste the docker service ls line you expect on a healthy system too, so the next person can tell converged from stuck without guessing.
Try this
Run these on a lab swarm (Docker 24 or newer is fine). Watch the REPLICAS column move from 3/3 to 5/5, then check which nodes the two new tasks landed on, so you know what a converged service looks like before you put real traffic in front of one.
$ docker service create --name web --replicas 3 -p 8080:80 nginx:1.27-alpinev1x2y3z4$ docker service lsID NAME MODE REPLICAS IMAGEv1x2y3z4 web replicated 3/3 nginx:1.27-alpine$ docker service scale web=5web scaled to 5$ docker service ps webID NAME IMAGE NODE DESIRED STATE CURRENT STATE… web.1 nginx… n1 Running Running 20s ago# STATUS: READY — replicas reconciled across nodes
Takeaway
Watch the fraction in the REPLICAS column, because it is the only place the swarm tells you whether it agrees with you yet. Change the spec, never the hosts. When the fraction stops moving, go straight to docker service ps and read the ERROR column. Choose replicated when the count is yours to pick and global when the answer is always one per node. And settle update parallelism, delay and failure action before the deploy that goes wrong, not during it.
docker service update --image web:1.28 --update-parallelism 1 --update-delay 15s --update-failure-action rollback web. Halfway through, one of the freshly started tasks fails its health check. What does the swarm do?docker service create --replicas 3 --name log-agent ... on a 3-node swarm and calls it one per node. Two weeks later they add a fourth node and the agent never shows up on it. What went wrong, and what should they have used?